| 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(DiagID: 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 {{.Strings: &BuiltinStrings, .Infos: 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 | |