1//===-- M68kSubtarget.cpp - M68k Subtarget Information ----------*- C++ -*-===//
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/// \file
10/// This file implements the M68k specific subclass of TargetSubtargetInfo.
11///
12//===----------------------------------------------------------------------===//
13
14#include "M68kSubtarget.h"
15#include "GISel/M68kCallLowering.h"
16#include "GISel/M68kLegalizerInfo.h"
17#include "GISel/M68kRegisterBankInfo.h"
18
19#include "M68k.h"
20#include "M68kMachineFunction.h"
21#include "M68kRegisterInfo.h"
22#include "M68kTargetMachine.h"
23
24#include "llvm/CodeGen/MachineJumpTableInfo.h"
25#include "llvm/IR/Attributes.h"
26#include "llvm/IR/Function.h"
27#include "llvm/MC/TargetRegistry.h"
28#include "llvm/Support/CommandLine.h"
29#include "llvm/Support/ErrorHandling.h"
30
31using namespace llvm;
32
33#define DEBUG_TYPE "m68k-subtarget"
34
35#define GET_SUBTARGETINFO_TARGET_DESC
36#define GET_SUBTARGETINFO_CTOR
37#include "M68kGenSubtargetInfo.inc"
38
39extern bool FixGlobalBaseReg;
40
41/// Select the M68k CPU for the given triple and cpu name.
42static StringRef selectM68kCPU(Triple TT, StringRef CPU) {
43  if (CPU.empty() || CPU == "generic") {
44    CPU = "M68000";
45  }
46  return CPU;
47}
48
49void M68kSubtarget::anchor() {}
50
51M68kSubtarget::M68kSubtarget(const Triple &TT, StringRef CPU, StringRef FS,
52                             const M68kTargetMachine &TM)
53    : M68kGenSubtargetInfo(TT, CPU, /*TuneCPU*/ CPU, FS), TM(TM), TSInfo(),
54      InstrInfo(initializeSubtargetDependencies(CPU, TT, FS, TM)),
55      FrameLowering(*this, this->getStackAlignment()), TLInfo(TM, *this),
56      TargetTriple(TT) {
57  CallLoweringInfo.reset(new M68kCallLowering(*getTargetLowering()));
58  Legalizer.reset(new M68kLegalizerInfo(*this));
59
60  auto *RBI = new M68kRegisterBankInfo(*getRegisterInfo());
61  RegBankInfo.reset(RBI);
62  InstSelector.reset(createM68kInstructionSelector(TM, *this, *RBI));
63}
64
65const CallLowering *M68kSubtarget::getCallLowering() const {
66  return CallLoweringInfo.get();
67}
68
69InstructionSelector *M68kSubtarget::getInstructionSelector() const {
70  return InstSelector.get();
71}
72
73const LegalizerInfo *M68kSubtarget::getLegalizerInfo() const {
74  return Legalizer.get();
75}
76
77const RegisterBankInfo *M68kSubtarget::getRegBankInfo() const {
78  return RegBankInfo.get();
79}
80
81bool M68kSubtarget::isPositionIndependent() const {
82  return TM.isPositionIndependent();
83}
84
85bool M68kSubtarget::isLegalToCallImmediateAddr() const { return true; }
86
87M68kSubtarget &M68kSubtarget::initializeSubtargetDependencies(
88    StringRef CPU, Triple TT, StringRef FS, const M68kTargetMachine &TM) {
89  std::string CPUName = selectM68kCPU(TT, CPU).str();
90
91  // Parse features string.
92  ParseSubtargetFeatures(CPUName, CPUName, FS);
93
94  // Initialize scheduling itinerary for the specified CPU.
95  InstrItins = getInstrItineraryForCPU(CPUName);
96
97  stackAlignment = 8;
98
99  return *this;
100}
101
102//===----------------------------------------------------------------------===//
103// Code Model
104//
105// Key assumptions:
106//  - Whenever possible we use pc-rel encoding since it is smaller(16 bit) than
107//    absolute(32 bit).
108//  - GOT is reachable within 16 bit offset for both Small and Medium models.
109//  - Code section is reachable within 16 bit offset for both models.
110//
111//  ---------------------+-------------------------+--------------------------
112//                       |          Small          |          Medium
113//                       +-------------------------+------------+-------------
114//                       |   Static   |    PIC     |   Static   |    PIC
115//  ---------------------+------------+------------+------------+-------------
116//                branch |   pc-rel   |   pc-rel   |   pc-rel   |   pc-rel
117//  ---------------------+------------+------------+------------+-------------
118//           call global |    @PLT    |    @PLT    |    @PLT    |    @PLT
119//  ---------------------+------------+------------+------------+-------------
120//         call internal |   pc-rel   |   pc-rel   |   pc-rel   |   pc-rel
121//  ---------------------+------------+------------+------------+-------------
122//            data local |   pc-rel   |   pc-rel   |  ~pc-rel   |  ^pc-rel
123//  ---------------------+------------+------------+------------+-------------
124//       data local big* |   pc-rel   |   pc-rel   |  absolute  |  @GOTOFF
125//  ---------------------+------------+------------+------------+-------------
126//           data global |   pc-rel   |  @GOTPCREL |  ~pc-rel   |  @GOTPCREL
127//  ---------------------+------------+------------+------------+-------------
128//      data global big* |   pc-rel   |  @GOTPCREL |  absolute  |  @GOTPCREL
129//  ---------------------+------------+------------+------------+-------------
130//
131// * Big data potentially cannot be reached within 16 bit offset and requires
132//   special handling for old(x00 and x10) CPUs. Normally these symbols go into
133//   separate .ldata section which mapped after normal .data and .text, but I
134//   don't really know how this must be done for M68k atm... will try to dig
135//   this info out from GCC. For now CPUs prior to M68020 will use static ref
136//   for Static Model and @GOT based references for PIC.
137//
138// ~ These are absolute for older CPUs for now.
139// ^ These are @GOTOFF for older CPUs for now.
140//===----------------------------------------------------------------------===//
141
142/// Classify a blockaddress reference for the current subtarget according to how
143/// we should reference it in a non-pcrel context.
144unsigned char M68kSubtarget::classifyBlockAddressReference() const {
145  // Unless we start to support Large Code Model branching is always pc-rel
146  return M68kII::MO_PC_RELATIVE_ADDRESS;
147}
148
149unsigned char
150M68kSubtarget::classifyLocalReference(const GlobalValue *GV) const {
151  switch (TM.getCodeModel()) {
152  default:
153    llvm_unreachable("Unsupported code model");
154  case CodeModel::Small:
155  case CodeModel::Kernel: {
156    return M68kII::MO_PC_RELATIVE_ADDRESS;
157  }
158  case CodeModel::Medium: {
159    if (isPositionIndependent()) {
160      // On M68020 and better we can fit big any data offset into dips field.
161      if (atLeastM68020()) {
162        return M68kII::MO_PC_RELATIVE_ADDRESS;
163      }
164      // Otherwise we could check the data size and make sure it will fit into
165      // 16 bit offset. For now we will be conservative and go with @GOTOFF
166      return M68kII::MO_GOTOFF;
167    } else {
168      if (atLeastM68020()) {
169        return M68kII::MO_PC_RELATIVE_ADDRESS;
170      }
171      return M68kII::MO_ABSOLUTE_ADDRESS;
172    }
173  }
174  }
175}
176
177unsigned char M68kSubtarget::classifyExternalReference(const Module &M) const {
178  if (TM.shouldAssumeDSOLocal(M, nullptr))
179    return classifyLocalReference(nullptr);
180
181  if (isPositionIndependent())
182    return M68kII::MO_GOTPCREL;
183
184  return M68kII::MO_GOT;
185}
186
187unsigned char
188M68kSubtarget::classifyGlobalReference(const GlobalValue *GV) const {
189  return classifyGlobalReference(GV, *GV->getParent());
190}
191
192unsigned char M68kSubtarget::classifyGlobalReference(const GlobalValue *GV,
193                                                     const Module &M) const {
194  if (TM.shouldAssumeDSOLocal(M, GV))
195    return classifyLocalReference(GV);
196
197  switch (TM.getCodeModel()) {
198  default:
199    llvm_unreachable("Unsupported code model");
200  case CodeModel::Small:
201  case CodeModel::Kernel: {
202    if (isPositionIndependent())
203      return M68kII::MO_GOTPCREL;
204    return M68kII::MO_PC_RELATIVE_ADDRESS;
205  }
206  case CodeModel::Medium: {
207    if (isPositionIndependent())
208      return M68kII::MO_GOTPCREL;
209
210    if (atLeastM68020())
211      return M68kII::MO_PC_RELATIVE_ADDRESS;
212
213    return M68kII::MO_ABSOLUTE_ADDRESS;
214  }
215  }
216}
217
218unsigned M68kSubtarget::getJumpTableEncoding() const {
219  if (isPositionIndependent()) {
220    // The only time we want to use GOTOFF(used when with EK_Custom32) is when
221    // the potential delta between the jump target and table base can be larger
222    // than displacement field, which is True for older CPUs(16 bit disp)
223    // in Medium model(can have large data way beyond 16 bit).
224    if (TM.getCodeModel() == CodeModel::Medium && !atLeastM68020())
225      return MachineJumpTableInfo::EK_Custom32;
226
227    return MachineJumpTableInfo::EK_LabelDifference32;
228  }
229
230  // In non-pic modes, just use the address of a block.
231  return MachineJumpTableInfo::EK_BlockAddress;
232}
233
234unsigned char
235M68kSubtarget::classifyGlobalFunctionReference(const GlobalValue *GV) const {
236  return classifyGlobalFunctionReference(GV, *GV->getParent());
237}
238
239unsigned char
240M68kSubtarget::classifyGlobalFunctionReference(const GlobalValue *GV,
241                                               const Module &M) const {
242  // local always use pc-rel referencing
243  if (TM.shouldAssumeDSOLocal(M, GV))
244    return M68kII::MO_NO_FLAG;
245
246  // If the function is marked as non-lazy, generate an indirect call
247  // which loads from the GOT directly. This avoids run-time overhead
248  // at the cost of eager binding.
249  auto *F = dyn_cast_or_null<Function>(GV);
250  if (F && F->hasFnAttribute(Attribute::NonLazyBind)) {
251    return M68kII::MO_GOTPCREL;
252  }
253
254  // otherwise linker will figure this out
255  return M68kII::MO_PLT;
256}
257