1//===-- Target.cpp ----------------------------------------------*- 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#include "../Target.h"
9#include "../Error.h"
10#include "MCTargetDesc/MipsABIInfo.h"
11#include "MCTargetDesc/MipsBaseInfo.h"
12#include "Mips.h"
13#include "MipsRegisterInfo.h"
14
15#define GET_AVAILABLE_OPCODE_CHECKER
16#include "MipsGenInstrInfo.inc"
17
18namespace llvm {
19namespace exegesis {
20
21#ifndef NDEBUG
22// Returns an error if we cannot handle the memory references in this
23// instruction.
24static Error isInvalidMemoryInstr(const Instruction &Instr) {
25 switch (Instr.Description.TSFlags & MipsII::FormMask) {
26 default:
27 llvm_unreachable("Unknown FormMask value");
28 // These have no memory access.
29 case MipsII::Pseudo:
30 case MipsII::FrmR:
31 case MipsII::FrmJ:
32 case MipsII::FrmFR:
33 return Error::success();
34 // These access memory and are handled.
35 case MipsII::FrmI:
36 return Error::success();
37 // These access memory and are not handled yet.
38 case MipsII::FrmFI:
39 case MipsII::FrmOther:
40 return make_error<Failure>("unsupported opcode: non uniform memory access");
41 }
42}
43#endif
44
45// Helper to fill a memory operand with a value.
46static void setMemOp(InstructionTemplate &IT, int OpIdx,
47 const MCOperand &OpVal) {
48 const auto Op = IT.getInstr().Operands[OpIdx];
49 assert(Op.isExplicit() && "invalid memory pattern");
50 IT.getValueFor(Op) = OpVal;
51}
52
53#include "MipsGenExegesis.inc"
54
55namespace {
56class ExegesisMipsTarget : public ExegesisTarget {
57public:
58 ExegesisMipsTarget()
59 : ExegesisTarget(MipsCpuPfmCounters, Mips_MC::isOpcodeAvailable) {}
60
61private:
62 MCRegister getScratchMemoryRegister(const Triple &TT) const override;
63 unsigned getMaxMemoryAccessSize() const override { return 64; }
64 void fillMemoryOperands(InstructionTemplate &IT, MCRegister Reg,
65 unsigned Offset) const override;
66
67 std::vector<MCInst> setRegTo(const MCSubtargetInfo &STI, MCRegister Reg,
68 const APInt &Value) const override;
69 bool matchesArch(Triple::ArchType Arch) const override {
70 return Arch == Triple::mips || Arch == Triple::mipsel ||
71 Arch == Triple::mips64 || Arch == Triple::mips64el;
72 }
73};
74} // end anonymous namespace
75
76// Generates instructions to load an immediate value into a register.
77static std::vector<MCInst> loadImmediate(MCRegister Reg, bool IsGPR32,
78 const APInt &Value) {
79 unsigned ZeroReg;
80 unsigned ORi, LUi, SLL;
81 if (IsGPR32) {
82 ZeroReg = Mips::R0;
83 ORi = Mips::ORi;
84 SLL = Mips::SLL;
85 LUi = Mips::LUi;
86 } else {
87 ZeroReg = Mips::R0_64;
88 ORi = Mips::ORi64;
89 SLL = Mips::SLL64_64;
90 LUi = Mips::LUi64;
91 }
92
93 if (Value.isIntN(N: 16)) {
94 return {MCInstBuilder(ORi)
95 .addReg(Reg)
96 .addReg(Reg: ZeroReg)
97 .addImm(Val: Value.getZExtValue())};
98 }
99
100 std::vector<MCInst> Instructions;
101 if (Value.isIntN(N: 32)) {
102 const uint16_t HiBits = Value.getHiBits(numBits: 16).getZExtValue();
103 if (!IsGPR32 && Value.getActiveBits() == 32) {
104 // Expand to an ORi instead of a LUi to avoid sign-extending into the
105 // upper 32 bits.
106 Instructions.push_back(
107 x: MCInstBuilder(ORi)
108 .addReg(Reg)
109 .addReg(Reg: ZeroReg)
110 .addImm(Val: HiBits));
111 Instructions.push_back(
112 x: MCInstBuilder(SLL)
113 .addReg(Reg)
114 .addReg(Reg)
115 .addImm(Val: 16));
116 } else {
117 Instructions.push_back(
118 x: MCInstBuilder(LUi)
119 .addReg(Reg)
120 .addImm(Val: HiBits));
121 }
122
123 const uint16_t LoBits = Value.getLoBits(numBits: 16).getZExtValue();
124 if (LoBits) {
125 Instructions.push_back(
126 x: MCInstBuilder(ORi)
127 .addReg(Reg)
128 .addReg(Reg: ZeroReg)
129 .addImm(Val: LoBits));
130 }
131
132 return Instructions;
133 }
134
135 llvm_unreachable("Not implemented for values wider than 32 bits");
136}
137
138MCRegister
139ExegesisMipsTarget::getScratchMemoryRegister(const Triple &TT) const {
140 const MipsABIInfo ABI =
141 TT.isArch64Bit() ? MipsABIInfo::N64() : MipsABIInfo::O32();
142 return ABI.getArgReg(I: 0);
143}
144
145void ExegesisMipsTarget::fillMemoryOperands(InstructionTemplate &IT,
146 MCRegister Reg,
147 unsigned Offset) const {
148 assert(!isInvalidMemoryInstr(IT.getInstr()) &&
149 "fillMemoryOperands requires a valid memory instruction");
150 setMemOp(IT, OpIdx: 0, OpVal: MCOperand::createReg(Reg: 0)); // IndexReg
151 setMemOp(IT, OpIdx: 1, OpVal: MCOperand::createReg(Reg)); // BaseReg
152 setMemOp(IT, OpIdx: 2, OpVal: MCOperand::createImm(Val: Offset)); // Disp
153}
154
155std::vector<MCInst> ExegesisMipsTarget::setRegTo(const MCSubtargetInfo &STI,
156 MCRegister Reg,
157 const APInt &Value) const {
158 if (Mips::GPR32RegClass.contains(Reg))
159 return loadImmediate(Reg, IsGPR32: true, Value);
160 if (Mips::GPR64RegClass.contains(Reg))
161 return loadImmediate(Reg, IsGPR32: false, Value);
162 errs() << "setRegTo is not implemented, results will be unreliable\n";
163 return {};
164}
165
166static ExegesisTarget *getTheExegesisMipsTarget() {
167 static ExegesisMipsTarget Target;
168 return &Target;
169}
170
171void InitializeMipsExegesisTarget() {
172 ExegesisTarget::registerTarget(T: getTheExegesisMipsTarget());
173}
174
175} // namespace exegesis
176} // namespace llvm
177