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>({
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
getABI() const43 StringRef WebAssemblyTargetInfo::getABI() const { return ABI; }
44
setABI(const std::string & Name)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
hasFeature(StringRef Feature) const53 bool WebAssemblyTargetInfo::hasFeature(StringRef Feature) const {
54 return llvm::StringSwitch<bool>(Feature)
55 .Case("atomics", HasAtomics)
56 .Case("bulk-memory", HasBulkMemory)
57 .Case("bulk-memory-opt", HasBulkMemoryOpt)
58 .Case("call-indirect-overlong", HasCallIndirectOverlong)
59 .Case("exception-handling", HasExceptionHandling)
60 .Case("extended-const", HasExtendedConst)
61 .Case("fp16", HasFP16)
62 .Case("multimemory", HasMultiMemory)
63 .Case("multivalue", HasMultivalue)
64 .Case("mutable-globals", HasMutableGlobals)
65 .Case("nontrapping-fptoint", HasNontrappingFPToInt)
66 .Case("reference-types", HasReferenceTypes)
67 .Case("relaxed-simd", SIMDLevel >= RelaxedSIMD)
68 .Case("sign-ext", HasSignExt)
69 .Case("simd128", SIMDLevel >= SIMD128)
70 .Case("tail-call", HasTailCall)
71 .Case("wide-arithmetic", HasWideArithmetic)
72 .Default(false);
73 }
74
isValidCPUName(StringRef Name) const75 bool WebAssemblyTargetInfo::isValidCPUName(StringRef Name) const {
76 return llvm::is_contained(ValidCPUNames, Name);
77 }
78
fillValidCPUList(SmallVectorImpl<StringRef> & Values) const79 void WebAssemblyTargetInfo::fillValidCPUList(
80 SmallVectorImpl<StringRef> &Values) const {
81 Values.append(std::begin(ValidCPUNames), std::end(ValidCPUNames));
82 }
83
getTargetDefines(const LangOptions & Opts,MacroBuilder & Builder) const84 void WebAssemblyTargetInfo::getTargetDefines(const LangOptions &Opts,
85 MacroBuilder &Builder) const {
86 defineCPUMacros(Builder, "wasm", /*Tuning=*/false);
87 if (HasAtomics)
88 Builder.defineMacro("__wasm_atomics__");
89 if (HasBulkMemory)
90 Builder.defineMacro("__wasm_bulk_memory__");
91 if (HasBulkMemoryOpt)
92 Builder.defineMacro("__wasm_bulk_memory_opt__");
93 if (HasExceptionHandling)
94 Builder.defineMacro("__wasm_exception_handling__");
95 if (HasExtendedConst)
96 Builder.defineMacro("__wasm_extended_const__");
97 if (HasMultiMemory)
98 Builder.defineMacro("__wasm_multimemory__");
99 if (HasFP16)
100 Builder.defineMacro("__wasm_fp16__");
101 if (HasMultivalue)
102 Builder.defineMacro("__wasm_multivalue__");
103 if (HasMutableGlobals)
104 Builder.defineMacro("__wasm_mutable_globals__");
105 if (HasNontrappingFPToInt)
106 Builder.defineMacro("__wasm_nontrapping_fptoint__");
107 if (HasReferenceTypes)
108 Builder.defineMacro("__wasm_reference_types__");
109 if (SIMDLevel >= RelaxedSIMD)
110 Builder.defineMacro("__wasm_relaxed_simd__");
111 if (HasSignExt)
112 Builder.defineMacro("__wasm_sign_ext__");
113 if (SIMDLevel >= SIMD128)
114 Builder.defineMacro("__wasm_simd128__");
115 if (HasTailCall)
116 Builder.defineMacro("__wasm_tail_call__");
117 if (HasWideArithmetic)
118 Builder.defineMacro("__wasm_wide_arithmetic__");
119
120 Builder.defineMacro("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1");
121 Builder.defineMacro("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2");
122 Builder.defineMacro("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4");
123 Builder.defineMacro("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8");
124 }
125
setSIMDLevel(llvm::StringMap<bool> & Features,SIMDEnum Level,bool Enabled)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
setFeatureEnabled(llvm::StringMap<bool> & Features,StringRef Name,bool Enabled) const153 void WebAssemblyTargetInfo::setFeatureEnabled(llvm::StringMap<bool> &Features,
154 StringRef Name,
155 bool Enabled) const {
156 if (Name == "simd128")
157 setSIMDLevel(Features, SIMD128, Enabled);
158 else if (Name == "relaxed-simd")
159 setSIMDLevel(Features, RelaxedSIMD, Enabled);
160 else
161 Features[Name] = Enabled;
162 }
163
initFeatureMap(llvm::StringMap<bool> & Features,DiagnosticsEngine & Diags,StringRef CPU,const std::vector<std::string> & FeaturesVec) const164 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, RelaxedSIMD, 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, FeaturesVec);
208 }
209
handleTargetFeatures(std::vector<std::string> & Features,DiagnosticsEngine & Diags)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(SIMDLevel, 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(SIMDLevel, RelaxedSIMD);
312 continue;
313 }
314 if (Feature == "-relaxed-simd") {
315 SIMDLevel = std::min(SIMDLevel, 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(SIMDLevel, SIMD128);
328 continue;
329 }
330 if (Feature == "-simd128") {
331 SIMDLevel = std::min(SIMDLevel, 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>
getTargetBuiltins() const371 WebAssemblyTargetInfo::getTargetBuiltins() const {
372 return {{&BuiltinStrings, BuiltinInfos}};
373 }
374
adjust(DiagnosticsEngine & Diags,LangOptions & Opts,const TargetInfo * Aux)375 void WebAssemblyTargetInfo::adjust(DiagnosticsEngine &Diags, LangOptions &Opts,
376 const TargetInfo *Aux) {
377 TargetInfo::adjust(Diags, Opts, Aux);
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
getTargetDefines(const LangOptions & Opts,MacroBuilder & Builder) const388 void WebAssembly32TargetInfo::getTargetDefines(const LangOptions &Opts,
389 MacroBuilder &Builder) const {
390 WebAssemblyTargetInfo::getTargetDefines(Opts, Builder);
391 defineCPUMacros(Builder, "wasm32", /*Tuning=*/false);
392 }
393
getTargetDefines(const LangOptions & Opts,MacroBuilder & Builder) const394 void WebAssembly64TargetInfo::getTargetDefines(const LangOptions &Opts,
395 MacroBuilder &Builder) const {
396 WebAssemblyTargetInfo::getTargetDefines(Opts, Builder);
397 defineCPUMacros(Builder, "wasm64", /*Tuning=*/false);
398 }
399