1//===--- TargetID.cpp - Utilities for parsing target ID -------------------===//
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#include "clang/Basic/TargetID.h"
10#include "clang/Basic/OffloadArch.h"
11#include "llvm/ADT/STLExtras.h"
12#include "llvm/ADT/SmallSet.h"
13#include "llvm/ADT/SmallVector.h"
14#include "llvm/Support/Path.h"
15#include "llvm/TargetParser/AMDGPUTargetParser.h"
16#include "llvm/TargetParser/Triple.h"
17#include <map>
18#include <optional>
19#include <string>
20
21namespace clang {
22
23static llvm::SmallVector<llvm::StringRef, 4>
24getAllPossibleAMDGPUTargetIDFeatures(const llvm::Triple &T,
25 llvm::StringRef Proc) {
26 // Entries in returned vector should be in alphabetical order.
27 llvm::SmallVector<llvm::StringRef, 4> Ret;
28 if (!T.isAMDGCN())
29 return Ret;
30 llvm::AMDGPU::GPUKind ProcKind = llvm::AMDGPU::parseArchAMDGCN(CPU: Proc);
31 if (ProcKind == llvm::AMDGPU::GK_NONE)
32 return Ret;
33 const llvm::AMDGPU::AMDGPUFeatureBitset &Features =
34 llvm::AMDGPU::getFeatureBitset(AK: ProcKind);
35 if (Features.test(I: llvm::AMDGPU::FEAT_SRAMECC_SUPPORT))
36 Ret.push_back(Elt: "sramecc");
37 // Only allow xnack in target ID if the processor supports on/off modes.
38 if (Features.test(I: llvm::AMDGPU::FEAT_XNACK_ON_OFF_MODES))
39 Ret.push_back(Elt: "xnack");
40 return Ret;
41}
42
43llvm::SmallVector<llvm::StringRef, 4>
44getAllPossibleTargetIDFeatures(const llvm::Triple &T,
45 llvm::StringRef Processor) {
46 llvm::SmallVector<llvm::StringRef, 4> Ret;
47 if (T.isAMDGPU())
48 return getAllPossibleAMDGPUTargetIDFeatures(T, Proc: Processor);
49 return Ret;
50}
51
52/// Returns canonical processor name or empty string if \p Processor is invalid.
53static llvm::StringRef getCanonicalProcessorName(const llvm::Triple &T,
54 llvm::StringRef Processor) {
55 if (!T.isAMDGPU())
56 return Processor;
57
58 if (llvm::StringRef Name = llvm::AMDGPU::getCanonicalArchName(T, Arch: Processor);
59 !Name.empty())
60 return Name;
61
62 // Accept the AMDGPU subarch triple spelling (e.g. "amdgpu9.00") as an alias
63 // for the corresponding gfx processor.
64 OffloadArch Arch =
65 getSubArchOffloadArch(SubArch: llvm::Triple::parseSubArch(Str: Processor));
66 if (Arch.isUnknown())
67 return {};
68 return OffloadArchToString(A: Arch);
69}
70
71llvm::StringRef getProcessorFromTargetID(const llvm::Triple &T,
72 llvm::StringRef TargetID) {
73 auto Split = TargetID.split(Separator: ':');
74 return getCanonicalProcessorName(T, Processor: Split.first);
75}
76
77// Parse a target ID with format checking only. Do not check whether processor
78// name or features are valid for the processor.
79//
80// A target ID is a processor name followed by a list of target features
81// delimited by colon. Each target feature is a string post-fixed by a plus
82// or minus sign, e.g. gfx908:sramecc+:xnack-.
83static std::optional<llvm::StringRef>
84parseTargetIDWithFormatCheckingOnly(llvm::StringRef TargetID,
85 llvm::StringMap<bool> *FeatureMap) {
86 llvm::StringRef Processor;
87
88 if (TargetID.empty())
89 return llvm::StringRef();
90
91 auto Split = TargetID.split(Separator: ':');
92 Processor = Split.first;
93 if (Processor.empty())
94 return std::nullopt;
95
96 auto Features = Split.second;
97 if (Features.empty())
98 return Processor;
99
100 llvm::StringMap<bool> LocalFeatureMap;
101 if (!FeatureMap)
102 FeatureMap = &LocalFeatureMap;
103
104 while (!Features.empty()) {
105 auto Splits = Features.split(Separator: ':');
106 if (Splits.first.empty())
107 return std::nullopt;
108 auto Sign = Splits.first.back();
109 auto Feature = Splits.first.drop_back();
110 if (Sign != '+' && Sign != '-')
111 return std::nullopt;
112 bool IsOn = Sign == '+';
113 // Each feature can only show up at most once in target ID.
114 if (!FeatureMap->try_emplace(Key: Feature, Args&: IsOn).second)
115 return std::nullopt;
116 Features = Splits.second;
117 }
118 return Processor;
119}
120
121std::optional<llvm::StringRef>
122parseTargetID(const llvm::Triple &T, llvm::StringRef TargetID,
123 llvm::StringMap<bool> *FeatureMap) {
124 auto OptionalProcessor =
125 parseTargetIDWithFormatCheckingOnly(TargetID, FeatureMap);
126
127 if (!OptionalProcessor)
128 return std::nullopt;
129
130 llvm::StringRef Processor = getCanonicalProcessorName(T, Processor: *OptionalProcessor);
131 if (Processor.empty())
132 return std::nullopt;
133
134 llvm::SmallSet<llvm::StringRef, 4> AllFeatures(
135 llvm::from_range, getAllPossibleTargetIDFeatures(T, Processor));
136
137 for (auto &&F : *FeatureMap)
138 if (!AllFeatures.count(V: F.first()))
139 return std::nullopt;
140
141 return Processor;
142}
143
144// A canonical target ID is a target ID containing a canonical processor name
145// and features in alphabetical order.
146std::string getCanonicalTargetID(llvm::StringRef Processor,
147 const llvm::StringMap<bool> &Features) {
148 std::string TargetID = Processor.str();
149 std::map<const llvm::StringRef, bool> OrderedMap;
150 for (const auto &F : Features)
151 OrderedMap[F.first()] = F.second;
152 for (const auto &F : OrderedMap)
153 TargetID = TargetID + ':' + F.first.str() + (F.second ? "+" : "-");
154 return TargetID;
155}
156
157// For a specific processor, a feature either shows up in all target IDs, or
158// does not show up in any target IDs. Otherwise the target ID combination
159// is invalid.
160std::optional<std::pair<llvm::StringRef, llvm::StringRef>>
161getConflictTargetIDCombination(const std::set<llvm::StringRef> &TargetIDs) {
162 struct Info {
163 llvm::StringRef TargetID;
164 llvm::StringMap<bool> Features;
165 Info(llvm::StringRef TargetID, const llvm::StringMap<bool> &Features)
166 : TargetID(TargetID), Features(Features) {}
167 };
168 llvm::StringMap<Info> FeatureMap;
169 for (auto &&ID : TargetIDs) {
170 llvm::StringMap<bool> Features;
171 llvm::StringRef Proc = *parseTargetIDWithFormatCheckingOnly(TargetID: ID, FeatureMap: &Features);
172 auto [Loc, Inserted] = FeatureMap.try_emplace(Key: Proc, Args: ID, Args&: Features);
173 if (!Inserted) {
174 auto &ExistingFeatures = Loc->second.Features;
175 if (llvm::any_of(Range&: Features, P: [&](auto &F) {
176 return ExistingFeatures.count(F.first()) == 0;
177 }))
178 return std::make_pair(x&: Loc->second.TargetID, y: ID);
179 }
180 }
181 return std::nullopt;
182}
183
184bool isCompatibleTargetID(llvm::StringRef Provided, llvm::StringRef Requested) {
185 llvm::StringMap<bool> ProvidedFeatures, RequestedFeatures;
186 llvm::StringRef ProvidedProc =
187 *parseTargetIDWithFormatCheckingOnly(TargetID: Provided, FeatureMap: &ProvidedFeatures);
188 llvm::StringRef RequestedProc =
189 *parseTargetIDWithFormatCheckingOnly(TargetID: Requested, FeatureMap: &RequestedFeatures);
190 if (ProvidedProc != RequestedProc)
191 return false;
192 for (const auto &F : ProvidedFeatures) {
193 auto Loc = RequestedFeatures.find(Key: F.first());
194 // The default (unspecified) value of a feature is 'All', which can match
195 // either 'On' or 'Off'.
196 if (Loc == RequestedFeatures.end())
197 return false;
198 // If a feature is specified, it must have exact match.
199 if (Loc->second != F.second)
200 return false;
201 }
202 return true;
203}
204
205std::string sanitizeTargetIDInFileName(llvm::StringRef TargetID) {
206 std::string FileName = TargetID.str();
207 if (llvm::sys::path::is_style_windows(S: llvm::sys::path::Style::native))
208 llvm::replace(Range&: FileName, OldValue: ':', NewValue: '@');
209 return FileName;
210}
211
212} // namespace clang
213