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 Builtin::Info BuiltinInfo[] = { |
24 | #define BUILTIN(ID, TYPE, ATTRS) \ |
25 | {#ID, TYPE, ATTRS, nullptr, HeaderDesc::NO_HEADER, ALL_LANGUAGES}, |
26 | #define TARGET_BUILTIN(ID, TYPE, ATTRS, FEATURE) \ |
27 | {#ID, TYPE, ATTRS, FEATURE, HeaderDesc::NO_HEADER, ALL_LANGUAGES}, |
28 | #define LIBBUILTIN(ID, TYPE, ATTRS, HEADER) \ |
29 | {#ID, TYPE, ATTRS, nullptr, HeaderDesc::HEADER, ALL_LANGUAGES}, |
30 | #include "clang/Basic/BuiltinsWebAssembly.def" |
31 | }; |
32 | |
33 | static constexpr llvm::StringLiteral ValidCPUNames[] = { |
34 | {"mvp" }, {"bleeding-edge" }, {"generic" }}; |
35 | |
36 | StringRef WebAssemblyTargetInfo::getABI() const { return ABI; } |
37 | |
38 | bool WebAssemblyTargetInfo::setABI(const std::string &Name) { |
39 | if (Name != "mvp" && Name != "experimental-mv" ) |
40 | return false; |
41 | |
42 | ABI = Name; |
43 | return true; |
44 | } |
45 | |
46 | bool WebAssemblyTargetInfo::hasFeature(StringRef Feature) const { |
47 | return llvm::StringSwitch<bool>(Feature) |
48 | .Case(S: "atomics" , Value: HasAtomics) |
49 | .Case(S: "bulk-memory" , Value: HasBulkMemory) |
50 | .Case(S: "exception-handling" , Value: HasExceptionHandling) |
51 | .Case(S: "extended-const" , Value: HasExtendedConst) |
52 | .Case(S: "half-precision" , Value: HasHalfPrecision) |
53 | .Case(S: "multimemory" , Value: HasMultiMemory) |
54 | .Case(S: "multivalue" , Value: HasMultivalue) |
55 | .Case(S: "mutable-globals" , Value: HasMutableGlobals) |
56 | .Case(S: "nontrapping-fptoint" , Value: HasNontrappingFPToInt) |
57 | .Case(S: "reference-types" , Value: HasReferenceTypes) |
58 | .Case(S: "relaxed-simd" , Value: SIMDLevel >= RelaxedSIMD) |
59 | .Case(S: "sign-ext" , Value: HasSignExt) |
60 | .Case(S: "simd128" , Value: SIMDLevel >= SIMD128) |
61 | .Case(S: "tail-call" , Value: HasTailCall) |
62 | .Default(Value: false); |
63 | } |
64 | |
65 | bool WebAssemblyTargetInfo::isValidCPUName(StringRef Name) const { |
66 | return llvm::is_contained(Range: ValidCPUNames, Element: Name); |
67 | } |
68 | |
69 | void WebAssemblyTargetInfo::fillValidCPUList( |
70 | SmallVectorImpl<StringRef> &Values) const { |
71 | Values.append(in_start: std::begin(arr: ValidCPUNames), in_end: std::end(arr: ValidCPUNames)); |
72 | } |
73 | |
74 | void WebAssemblyTargetInfo::getTargetDefines(const LangOptions &Opts, |
75 | MacroBuilder &Builder) const { |
76 | defineCPUMacros(Builder, CPUName: "wasm" , /*Tuning=*/false); |
77 | if (HasAtomics) |
78 | Builder.defineMacro(Name: "__wasm_atomics__" ); |
79 | if (HasBulkMemory) |
80 | Builder.defineMacro(Name: "__wasm_bulk_memory__" ); |
81 | if (HasExceptionHandling) |
82 | Builder.defineMacro(Name: "__wasm_exception_handling__" ); |
83 | if (HasExtendedConst) |
84 | Builder.defineMacro(Name: "__wasm_extended_const__" ); |
85 | if (HasMultiMemory) |
86 | Builder.defineMacro(Name: "__wasm_multimemory__" ); |
87 | if (HasHalfPrecision) |
88 | Builder.defineMacro(Name: "__wasm_half_precision__" ); |
89 | if (HasMultivalue) |
90 | Builder.defineMacro(Name: "__wasm_multivalue__" ); |
91 | if (HasMutableGlobals) |
92 | Builder.defineMacro(Name: "__wasm_mutable_globals__" ); |
93 | if (HasNontrappingFPToInt) |
94 | Builder.defineMacro(Name: "__wasm_nontrapping_fptoint__" ); |
95 | if (HasReferenceTypes) |
96 | Builder.defineMacro(Name: "__wasm_reference_types__" ); |
97 | if (SIMDLevel >= RelaxedSIMD) |
98 | Builder.defineMacro(Name: "__wasm_relaxed_simd__" ); |
99 | if (HasSignExt) |
100 | Builder.defineMacro(Name: "__wasm_sign_ext__" ); |
101 | if (SIMDLevel >= SIMD128) |
102 | Builder.defineMacro(Name: "__wasm_simd128__" ); |
103 | if (HasTailCall) |
104 | Builder.defineMacro(Name: "__wasm_tail_call__" ); |
105 | |
106 | Builder.defineMacro(Name: "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1" ); |
107 | Builder.defineMacro(Name: "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2" ); |
108 | Builder.defineMacro(Name: "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4" ); |
109 | Builder.defineMacro(Name: "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8" ); |
110 | } |
111 | |
112 | void WebAssemblyTargetInfo::setSIMDLevel(llvm::StringMap<bool> &Features, |
113 | SIMDEnum Level, bool Enabled) { |
114 | if (Enabled) { |
115 | switch (Level) { |
116 | case RelaxedSIMD: |
117 | Features["relaxed-simd" ] = true; |
118 | [[fallthrough]]; |
119 | case SIMD128: |
120 | Features["simd128" ] = true; |
121 | [[fallthrough]]; |
122 | case NoSIMD: |
123 | break; |
124 | } |
125 | return; |
126 | } |
127 | |
128 | switch (Level) { |
129 | case NoSIMD: |
130 | case SIMD128: |
131 | Features["simd128" ] = false; |
132 | [[fallthrough]]; |
133 | case RelaxedSIMD: |
134 | Features["relaxed-simd" ] = false; |
135 | break; |
136 | } |
137 | } |
138 | |
139 | void WebAssemblyTargetInfo::setFeatureEnabled(llvm::StringMap<bool> &Features, |
140 | StringRef Name, |
141 | bool Enabled) const { |
142 | if (Name == "simd128" ) |
143 | setSIMDLevel(Features, Level: SIMD128, Enabled); |
144 | else if (Name == "relaxed-simd" ) |
145 | setSIMDLevel(Features, Level: RelaxedSIMD, Enabled); |
146 | else |
147 | Features[Name] = Enabled; |
148 | } |
149 | |
150 | bool WebAssemblyTargetInfo::initFeatureMap( |
151 | llvm::StringMap<bool> &Features, DiagnosticsEngine &Diags, StringRef CPU, |
152 | const std::vector<std::string> &FeaturesVec) const { |
153 | auto addGenericFeatures = [&]() { |
154 | Features["multivalue" ] = true; |
155 | Features["mutable-globals" ] = true; |
156 | Features["reference-types" ] = true; |
157 | Features["sign-ext" ] = true; |
158 | }; |
159 | auto addBleedingEdgeFeatures = [&]() { |
160 | addGenericFeatures(); |
161 | Features["atomics" ] = true; |
162 | Features["bulk-memory" ] = true; |
163 | Features["exception-handling" ] = true; |
164 | Features["extended-const" ] = true; |
165 | Features["half-precision" ] = true; |
166 | Features["multimemory" ] = true; |
167 | Features["nontrapping-fptoint" ] = true; |
168 | Features["tail-call" ] = true; |
169 | setSIMDLevel(Features, Level: RelaxedSIMD, Enabled: true); |
170 | }; |
171 | if (CPU == "generic" ) { |
172 | addGenericFeatures(); |
173 | } else if (CPU == "bleeding-edge" ) { |
174 | addBleedingEdgeFeatures(); |
175 | } |
176 | |
177 | return TargetInfo::initFeatureMap(Features, Diags, CPU, FeatureVec: FeaturesVec); |
178 | } |
179 | |
180 | bool WebAssemblyTargetInfo::handleTargetFeatures( |
181 | std::vector<std::string> &Features, DiagnosticsEngine &Diags) { |
182 | for (const auto &Feature : Features) { |
183 | if (Feature == "+atomics" ) { |
184 | HasAtomics = true; |
185 | continue; |
186 | } |
187 | if (Feature == "-atomics" ) { |
188 | HasAtomics = false; |
189 | continue; |
190 | } |
191 | if (Feature == "+bulk-memory" ) { |
192 | HasBulkMemory = true; |
193 | continue; |
194 | } |
195 | if (Feature == "-bulk-memory" ) { |
196 | HasBulkMemory = false; |
197 | continue; |
198 | } |
199 | if (Feature == "+exception-handling" ) { |
200 | HasExceptionHandling = true; |
201 | continue; |
202 | } |
203 | if (Feature == "-exception-handling" ) { |
204 | HasExceptionHandling = false; |
205 | continue; |
206 | } |
207 | if (Feature == "+extended-const" ) { |
208 | HasExtendedConst = true; |
209 | continue; |
210 | } |
211 | if (Feature == "-extended-const" ) { |
212 | HasExtendedConst = false; |
213 | continue; |
214 | } |
215 | if (Feature == "+half-precision" ) { |
216 | SIMDLevel = std::max(a: SIMDLevel, b: SIMD128); |
217 | HasHalfPrecision = true; |
218 | continue; |
219 | } |
220 | if (Feature == "-half-precision" ) { |
221 | HasHalfPrecision = false; |
222 | continue; |
223 | } |
224 | if (Feature == "+multimemory" ) { |
225 | HasMultiMemory = true; |
226 | continue; |
227 | } |
228 | if (Feature == "-multimemory" ) { |
229 | HasMultiMemory = false; |
230 | continue; |
231 | } |
232 | if (Feature == "+multivalue" ) { |
233 | HasMultivalue = true; |
234 | continue; |
235 | } |
236 | if (Feature == "-multivalue" ) { |
237 | HasMultivalue = false; |
238 | continue; |
239 | } |
240 | if (Feature == "+mutable-globals" ) { |
241 | HasMutableGlobals = true; |
242 | continue; |
243 | } |
244 | if (Feature == "-mutable-globals" ) { |
245 | HasMutableGlobals = false; |
246 | continue; |
247 | } |
248 | if (Feature == "+nontrapping-fptoint" ) { |
249 | HasNontrappingFPToInt = true; |
250 | continue; |
251 | } |
252 | if (Feature == "-nontrapping-fptoint" ) { |
253 | HasNontrappingFPToInt = false; |
254 | continue; |
255 | } |
256 | if (Feature == "+reference-types" ) { |
257 | HasReferenceTypes = true; |
258 | continue; |
259 | } |
260 | if (Feature == "-reference-types" ) { |
261 | HasReferenceTypes = false; |
262 | continue; |
263 | } |
264 | if (Feature == "+relaxed-simd" ) { |
265 | SIMDLevel = std::max(a: SIMDLevel, b: RelaxedSIMD); |
266 | continue; |
267 | } |
268 | if (Feature == "-relaxed-simd" ) { |
269 | SIMDLevel = std::min(a: SIMDLevel, b: SIMDEnum(RelaxedSIMD - 1)); |
270 | continue; |
271 | } |
272 | if (Feature == "+sign-ext" ) { |
273 | HasSignExt = true; |
274 | continue; |
275 | } |
276 | if (Feature == "-sign-ext" ) { |
277 | HasSignExt = false; |
278 | continue; |
279 | } |
280 | if (Feature == "+simd128" ) { |
281 | SIMDLevel = std::max(a: SIMDLevel, b: SIMD128); |
282 | continue; |
283 | } |
284 | if (Feature == "-simd128" ) { |
285 | SIMDLevel = std::min(a: SIMDLevel, b: SIMDEnum(SIMD128 - 1)); |
286 | continue; |
287 | } |
288 | if (Feature == "+tail-call" ) { |
289 | HasTailCall = true; |
290 | continue; |
291 | } |
292 | if (Feature == "-tail-call" ) { |
293 | HasTailCall = false; |
294 | continue; |
295 | } |
296 | |
297 | Diags.Report(DiagID: diag::err_opt_not_valid_with_opt) |
298 | << Feature << "-target-feature" ; |
299 | return false; |
300 | } |
301 | return true; |
302 | } |
303 | |
304 | ArrayRef<Builtin::Info> WebAssemblyTargetInfo::getTargetBuiltins() const { |
305 | return llvm::ArrayRef(BuiltinInfo, clang::WebAssembly::LastTSBuiltin - |
306 | Builtin::FirstTSBuiltin); |
307 | } |
308 | |
309 | void WebAssemblyTargetInfo::adjust(DiagnosticsEngine &Diags, |
310 | LangOptions &Opts) { |
311 | TargetInfo::adjust(Diags, Opts); |
312 | // Turn off POSIXThreads and ThreadModel so that we don't predefine _REENTRANT |
313 | // or __STDCPP_THREADS__ if we will eventually end up stripping atomics |
314 | // because they are unsupported. |
315 | if (!HasAtomics || !HasBulkMemory) { |
316 | Opts.POSIXThreads = false; |
317 | Opts.setThreadModel(LangOptions::ThreadModelKind::Single); |
318 | Opts.ThreadsafeStatics = false; |
319 | } |
320 | } |
321 | |
322 | void WebAssembly32TargetInfo::getTargetDefines(const LangOptions &Opts, |
323 | MacroBuilder &Builder) const { |
324 | WebAssemblyTargetInfo::getTargetDefines(Opts, Builder); |
325 | defineCPUMacros(Builder, CPUName: "wasm32" , /*Tuning=*/false); |
326 | } |
327 | |
328 | void WebAssembly64TargetInfo::getTargetDefines(const LangOptions &Opts, |
329 | MacroBuilder &Builder) const { |
330 | WebAssemblyTargetInfo::getTargetDefines(Opts, Builder); |
331 | defineCPUMacros(Builder, CPUName: "wasm64" , /*Tuning=*/false); |
332 | } |
333 | |