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 "AArch64.h" |
10 | #include "AArch64RegisterInfo.h" |
11 | |
12 | #if defined(__aarch64__) && defined(__linux__) |
13 | #include <linux/prctl.h> // For PR_PAC_* constants |
14 | #include <sys/prctl.h> |
15 | #ifndef PR_PAC_APIAKEY |
16 | #define PR_PAC_APIAKEY (1UL << 0) |
17 | #endif |
18 | #ifndef PR_PAC_APIBKEY |
19 | #define PR_PAC_APIBKEY (1UL << 1) |
20 | #endif |
21 | #ifndef PR_PAC_APDAKEY |
22 | #define PR_PAC_APDAKEY (1UL << 2) |
23 | #endif |
24 | #ifndef PR_PAC_APDBKEY |
25 | #define PR_PAC_APDBKEY (1UL << 3) |
26 | #endif |
27 | #endif |
28 | |
29 | #define GET_AVAILABLE_OPCODE_CHECKER |
30 | #include "AArch64GenInstrInfo.inc" |
31 | |
32 | namespace llvm { |
33 | namespace exegesis { |
34 | |
35 | static unsigned getLoadImmediateOpcode(unsigned RegBitWidth) { |
36 | switch (RegBitWidth) { |
37 | case 32: |
38 | return AArch64::MOVi32imm; |
39 | case 64: |
40 | return AArch64::MOVi64imm; |
41 | } |
42 | llvm_unreachable("Invalid Value Width" ); |
43 | } |
44 | |
45 | // Generates instruction to load an immediate value into a register. |
46 | static MCInst loadImmediate(MCRegister Reg, unsigned RegBitWidth, |
47 | const APInt &Value) { |
48 | assert(Value.getBitWidth() <= RegBitWidth && |
49 | "Value must fit in the Register" ); |
50 | return MCInstBuilder(getLoadImmediateOpcode(RegBitWidth)) |
51 | .addReg(Reg) |
52 | .addImm(Val: Value.getZExtValue()); |
53 | } |
54 | |
55 | static MCInst loadZPRImmediate(MCRegister Reg, unsigned RegBitWidth, |
56 | const APInt &Value) { |
57 | assert(Value.getZExtValue() < (1 << 7) && |
58 | "Value must be in the range of the immediate opcode" ); |
59 | return MCInstBuilder(AArch64::DUP_ZI_D) |
60 | .addReg(Reg) |
61 | .addImm(Value.getZExtValue()) |
62 | .addImm(0); |
63 | } |
64 | |
65 | static MCInst loadPPRImmediate(MCRegister Reg, unsigned RegBitWidth, |
66 | const APInt &Value) { |
67 | // For PPR, we typically use PTRUE instruction to set predicate registers |
68 | return MCInstBuilder(AArch64::PTRUE_B) |
69 | .addReg(Reg) |
70 | .addImm(31); // All lanes true for 16 bits |
71 | } |
72 | |
73 | // Generates instructions to load an immediate value into an FPCR register. |
74 | static std::vector<MCInst> |
75 | loadFPCRImmediate(MCRegister Reg, unsigned RegBitWidth, const APInt &Value) { |
76 | MCRegister TempReg = AArch64::X8; |
77 | MCInst LoadImm = MCInstBuilder(AArch64::MOVi64imm).addReg(TempReg).addImm(0); |
78 | MCInst MoveToFPCR = |
79 | MCInstBuilder(AArch64::MSR).addImm(AArch64SysReg::FPCR).addReg(TempReg); |
80 | return {LoadImm, MoveToFPCR}; |
81 | } |
82 | |
83 | // Fetch base-instruction to load an FP immediate value into a register. |
84 | static unsigned getLoadFPImmediateOpcode(unsigned RegBitWidth) { |
85 | switch (RegBitWidth) { |
86 | case 16: |
87 | return AArch64::FMOVH0; // FMOVHi; |
88 | case 32: |
89 | return AArch64::FMOVS0; // FMOVSi; |
90 | case 64: |
91 | return AArch64::MOVID; // FMOVDi; |
92 | case 128: |
93 | return AArch64::MOVIv2d_ns; |
94 | } |
95 | llvm_unreachable("Invalid Value Width" ); |
96 | } |
97 | |
98 | // Generates instruction to load an FP immediate value into a register. |
99 | static MCInst loadFPImmediate(MCRegister Reg, unsigned RegBitWidth, |
100 | const APInt &Value) { |
101 | assert(Value.getZExtValue() == 0 && "Expected initialisation value 0" ); |
102 | MCInst Instructions = |
103 | MCInstBuilder(getLoadFPImmediateOpcode(RegBitWidth)).addReg(Reg); |
104 | if (RegBitWidth >= 64) |
105 | Instructions.addOperand(Op: MCOperand::createImm(Val: Value.getZExtValue())); |
106 | return Instructions; |
107 | } |
108 | |
109 | #include "AArch64GenExegesis.inc" |
110 | |
111 | namespace { |
112 | |
113 | class ExegesisAArch64Target : public ExegesisTarget { |
114 | public: |
115 | ExegesisAArch64Target() |
116 | : ExegesisTarget(AArch64CpuPfmCounters, AArch64_MC::isOpcodeAvailable) {} |
117 | |
118 | private: |
119 | std::vector<MCInst> setRegTo(const MCSubtargetInfo &STI, MCRegister Reg, |
120 | const APInt &Value) const override { |
121 | if (AArch64::GPR32RegClass.contains(Reg)) |
122 | return {loadImmediate(Reg, RegBitWidth: 32, Value)}; |
123 | if (AArch64::GPR64RegClass.contains(Reg)) |
124 | return {loadImmediate(Reg, RegBitWidth: 64, Value)}; |
125 | if (AArch64::PPRRegClass.contains(Reg)) |
126 | return {loadPPRImmediate(Reg, RegBitWidth: 16, Value)}; |
127 | if (AArch64::FPR8RegClass.contains(Reg)) |
128 | return {loadFPImmediate(Reg - AArch64::B0 + AArch64::D0, 64, Value)}; |
129 | if (AArch64::FPR16RegClass.contains(Reg)) |
130 | return {loadFPImmediate(Reg, RegBitWidth: 16, Value)}; |
131 | if (AArch64::FPR32RegClass.contains(Reg)) |
132 | return {loadFPImmediate(Reg, RegBitWidth: 32, Value)}; |
133 | if (AArch64::FPR64RegClass.contains(Reg)) |
134 | return {loadFPImmediate(Reg, RegBitWidth: 64, Value)}; |
135 | if (AArch64::FPR128RegClass.contains(Reg)) |
136 | return {loadFPImmediate(Reg, RegBitWidth: 128, Value)}; |
137 | if (AArch64::ZPRRegClass.contains(Reg)) |
138 | return {loadZPRImmediate(Reg, RegBitWidth: 128, Value)}; |
139 | if (Reg == AArch64::FPCR) |
140 | return {loadFPCRImmediate(Reg, RegBitWidth: 32, Value)}; |
141 | |
142 | errs() << "setRegTo is not implemented, results will be unreliable\n" ; |
143 | return {}; |
144 | } |
145 | |
146 | bool matchesArch(Triple::ArchType Arch) const override { |
147 | return Arch == Triple::aarch64 || Arch == Triple::aarch64_be; |
148 | } |
149 | |
150 | void addTargetSpecificPasses(PassManagerBase &PM) const override { |
151 | // Function return is a pseudo-instruction that needs to be expanded |
152 | PM.add(P: createAArch64ExpandPseudoPass()); |
153 | } |
154 | }; |
155 | |
156 | } // namespace |
157 | |
158 | static ExegesisTarget *getTheExegesisAArch64Target() { |
159 | static ExegesisAArch64Target Target; |
160 | return &Target; |
161 | } |
162 | |
163 | void InitializeAArch64ExegesisTarget() { |
164 | ExegesisTarget::registerTarget(T: getTheExegesisAArch64Target()); |
165 | } |
166 | |
167 | } // namespace exegesis |
168 | } // namespace llvm |
169 | |