1//===--- M68k.cpp - Implement M68k targets 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 M68k TargetInfo objects.
10//
11//===----------------------------------------------------------------------===//
12
13#include "M68k.h"
14#include "clang/Basic/Builtins.h"
15#include "clang/Basic/Diagnostic.h"
16#include "llvm/ADT/StringRef.h"
17#include "llvm/ADT/StringSwitch.h"
18#include "llvm/TargetParser/TargetParser.h"
19#include <cstdint>
20#include <cstring>
21#include <limits>
22#include <optional>
23
24namespace clang {
25namespace targets {
26
27// Linux/m68k ("old") uses the ABI from Sun Microsystems a.out
28// for m68k, and uses 16-bit alignments for int/long/pointers.
29//
30// NetBSD/m68k uses the ABI from SVR4 for m68k,
31// which uses 32-bit alignments for int/long/pointers.
32//
33// Ref. https://github.com/M680x0/issues/issues/13 and
34// https://github.com/llvm/llvm-project/issues/199826
35//
36// Arguably this ought to respect -malign-int, as suggested
37// above, this code doesn't so far.
38
39M68kTargetInfo::M68kTargetInfo(const llvm::Triple &Triple,
40 const TargetOptions &Opts)
41 : TargetInfo(Triple), TargetOpts(Opts) {
42 resetDataLayout();
43
44 SizeType = UnsignedInt;
45 PtrDiffType = SignedInt;
46 IntPtrType = SignedInt;
47 if (getTriple().isOSNetBSD()) {
48 IntAlign = LongAlign = PointerAlign = 32;
49 } else {
50 IntAlign = LongAlign = PointerAlign = 16;
51 }
52}
53
54bool M68kTargetInfo::setCPU(StringRef Name) {
55 CPU = llvm::StringSwitch<CPUKind>(Name)
56 .Case(S: "generic", Value: CK_68000)
57 .Case(S: "M68000", Value: CK_68000)
58 .Case(S: "M68010", Value: CK_68010)
59 .Case(S: "M68020", Value: CK_68020)
60 .Case(S: "M68030", Value: CK_68030)
61 .Case(S: "M68040", Value: CK_68040)
62 .Case(S: "M68060", Value: CK_68060)
63 .Default(Value: CK_Unknown);
64 return CPU != CK_Unknown;
65}
66
67void M68kTargetInfo::getTargetDefines(const LangOptions &Opts,
68 MacroBuilder &Builder) const {
69 using llvm::Twine;
70
71 Builder.defineMacro(Name: "__m68k__");
72
73 DefineStd(Builder, MacroName: "mc68000", Opts);
74
75 // For sub-architecture
76 switch (CPU) {
77 case CK_68010:
78 DefineStd(Builder, MacroName: "mc68010", Opts);
79 break;
80 case CK_68020:
81 DefineStd(Builder, MacroName: "mc68020", Opts);
82 break;
83 case CK_68030:
84 DefineStd(Builder, MacroName: "mc68030", Opts);
85 break;
86 case CK_68040:
87 DefineStd(Builder, MacroName: "mc68040", Opts);
88 break;
89 case CK_68060:
90 DefineStd(Builder, MacroName: "mc68060", Opts);
91 break;
92 default:
93 break;
94 }
95
96 if (CPU >= CK_68020) {
97 Builder.defineMacro(Name: "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1");
98 Builder.defineMacro(Name: "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2");
99 Builder.defineMacro(Name: "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4");
100 }
101
102 // Floating point
103 if (TargetOpts.FeatureMap.lookup(Key: "isa-68881") ||
104 TargetOpts.FeatureMap.lookup(Key: "isa-68882"))
105 Builder.defineMacro(Name: "__HAVE_68881__");
106}
107
108llvm::SmallVector<Builtin::InfosShard>
109M68kTargetInfo::getTargetBuiltins() const {
110 // FIXME: Implement.
111 return {};
112}
113
114bool M68kTargetInfo::hasFeature(StringRef Feature) const {
115 // FIXME elaborate moar
116 return Feature == "M68000";
117}
118
119const char *const M68kTargetInfo::GCCRegNames[] = {
120 "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
121 "a0", "a1", "a2", "a3", "a4", "a5", "a6", "sp",
122 "pc"};
123
124ArrayRef<const char *> M68kTargetInfo::getGCCRegNames() const {
125 return llvm::ArrayRef(GCCRegNames);
126}
127
128const TargetInfo::GCCRegAlias M68kTargetInfo::GCCRegAliases[] = {
129 {.Aliases: {"bp"}, .Register: "a5"},
130 {.Aliases: {"fp"}, .Register: "a6"},
131 {.Aliases: {"usp", "ssp", "isp", "a7"}, .Register: "sp"},
132};
133
134ArrayRef<TargetInfo::GCCRegAlias> M68kTargetInfo::getGCCRegAliases() const {
135 return llvm::ArrayRef(GCCRegAliases);
136}
137
138bool M68kTargetInfo::validateAsmConstraint(
139 const char *&Name, TargetInfo::ConstraintInfo &info) const {
140 switch (*Name) {
141 case 'a': // address register
142 case 'd': // data register
143 info.setAllowsRegister();
144 return true;
145 case 'I': // constant integer in the range [1,8]
146 info.setRequiresImmediate(Min: 1, Max: 8);
147 return true;
148 case 'J': // constant signed 16-bit integer
149 info.setRequiresImmediate(Min: std::numeric_limits<int16_t>::min(),
150 Max: std::numeric_limits<int16_t>::max());
151 return true;
152 case 'K': // constant that is NOT in the range of [-0x80, 0x80)
153 info.setRequiresImmediate();
154 return true;
155 case 'L': // constant integer in the range [-8,-1]
156 info.setRequiresImmediate(Min: -8, Max: -1);
157 return true;
158 case 'M': // constant that is NOT in the range of [-0x100, 0x100]
159 info.setRequiresImmediate();
160 return true;
161 case 'N': // constant integer in the range [24,31]
162 info.setRequiresImmediate(Min: 24, Max: 31);
163 return true;
164 case 'O': // constant integer 16
165 info.setRequiresImmediate(16);
166 return true;
167 case 'P': // constant integer in the range [8,15]
168 info.setRequiresImmediate(Min: 8, Max: 15);
169 return true;
170 case 'C':
171 ++Name;
172 switch (*Name) {
173 case '0': // constant integer 0
174 info.setRequiresImmediate(0);
175 return true;
176 case 'i': // constant integer
177 case 'j': // integer constant that doesn't fit in 16 bits
178 info.setRequiresImmediate();
179 return true;
180 default:
181 break;
182 }
183 break;
184 case 'Q': // address register indirect addressing
185 case 'U': // address register indirect w/ constant offset addressing
186 // TODO: Handle 'S' (basically 'm' when pc-rel is enforced) when
187 // '-mpcrel' flag is properly handled by the driver.
188 info.setAllowsMemory();
189 return true;
190 default:
191 break;
192 }
193 return false;
194}
195
196std::optional<std::string>
197M68kTargetInfo::handleAsmEscapedChar(char EscChar) const {
198 char C;
199 switch (EscChar) {
200 case '.':
201 case '#':
202 C = EscChar;
203 break;
204 case '/':
205 C = '%';
206 break;
207 case '$':
208 C = 's';
209 break;
210 case '&':
211 C = 'd';
212 break;
213 default:
214 return std::nullopt;
215 }
216
217 return std::string(1, C);
218}
219
220std::string M68kTargetInfo::convertConstraint(const char *&Constraint) const {
221 if (*Constraint == 'C')
222 // Two-character constraint; add "^" hint for later parsing
223 return std::string("^") + std::string(Constraint++, 2);
224
225 return std::string(1, *Constraint);
226}
227
228std::string_view M68kTargetInfo::getClobbers() const {
229 // FIXME: Is this really right?
230 return "";
231}
232
233TargetInfo::BuiltinVaListKind M68kTargetInfo::getBuiltinVaListKind() const {
234 return TargetInfo::VoidPtrBuiltinVaList;
235}
236
237TargetInfo::CallingConvCheckResult
238M68kTargetInfo::checkCallingConvention(CallingConv CC) const {
239 switch (CC) {
240 case CC_C:
241 case CC_M68kRTD:
242 return CCCR_OK;
243 default:
244 return TargetInfo::checkCallingConvention(CC);
245 }
246}
247} // namespace targets
248} // namespace clang
249