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 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("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 75 bool WebAssemblyTargetInfo::isValidCPUName(StringRef Name) const { 76 return llvm::is_contained(ValidCPUNames, Name); 77 } 78 79 void WebAssemblyTargetInfo::fillValidCPUList( 80 SmallVectorImpl<StringRef> &Values) const { 81 Values.append(std::begin(ValidCPUNames), std::end(ValidCPUNames)); 82 } 83 84 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 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, SIMD128, Enabled); 158 else if (Name == "relaxed-simd") 159 setSIMDLevel(Features, 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, 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 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> 371 WebAssemblyTargetInfo::getTargetBuiltins() const { 372 return {{&BuiltinStrings, BuiltinInfos}}; 373 } 374 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 388 void WebAssembly32TargetInfo::getTargetDefines(const LangOptions &Opts, 389 MacroBuilder &Builder) const { 390 WebAssemblyTargetInfo::getTargetDefines(Opts, Builder); 391 defineCPUMacros(Builder, "wasm32", /*Tuning=*/false); 392 } 393 394 void WebAssembly64TargetInfo::getTargetDefines(const LangOptions &Opts, 395 MacroBuilder &Builder) const { 396 WebAssemblyTargetInfo::getTargetDefines(Opts, Builder); 397 defineCPUMacros(Builder, "wasm64", /*Tuning=*/false); 398 } 399