1 | //===- X86SuppressAPXForReloc.cpp - Suppress APX features for relocations -===// |
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 | /// \file |
9 | /// |
10 | /// This pass is added to suppress APX features for relocations. It's used to |
11 | /// keep backward compatibility with old version of linker having no APX |
12 | /// support. It can be removed after APX support is included in the default |
13 | /// linker on OS. |
14 | /// |
15 | //===----------------------------------------------------------------------===// |
16 | |
17 | #include "X86.h" |
18 | #include "X86InstrInfo.h" |
19 | #include "X86RegisterInfo.h" |
20 | #include "X86Subtarget.h" |
21 | |
22 | #include "llvm/CodeGen/MachineFunctionPass.h" |
23 | #include "llvm/CodeGen/MachineInstrBuilder.h" |
24 | #include "llvm/CodeGen/MachineOperand.h" |
25 | #include "llvm/CodeGen/MachineRegisterInfo.h" |
26 | #include "llvm/CodeGen/Passes.h" |
27 | #include "llvm/CodeGen/TargetRegisterInfo.h" |
28 | #include "llvm/Support/ErrorHandling.h" |
29 | |
30 | using namespace llvm; |
31 | |
32 | #define DEBUG_TYPE "x86-suppress-apx-for-relocation" |
33 | |
34 | cl::opt<bool> X86EnableAPXForRelocation( |
35 | "x86-enable-apx-for-relocation" , |
36 | cl::desc("Enable APX features (EGPR, NDD and NF) for instructions with " |
37 | "relocations on x86-64 ELF" ), |
38 | cl::init(Val: false)); |
39 | |
40 | namespace { |
41 | class X86SuppressAPXForRelocationPass : public MachineFunctionPass { |
42 | public: |
43 | X86SuppressAPXForRelocationPass() : MachineFunctionPass(ID) {} |
44 | |
45 | StringRef getPassName() const override { |
46 | return "X86 Suppress APX features for relocation" ; |
47 | } |
48 | |
49 | bool runOnMachineFunction(MachineFunction &MF) override; |
50 | |
51 | static char ID; |
52 | }; |
53 | } // namespace |
54 | |
55 | char X86SuppressAPXForRelocationPass::ID = 0; |
56 | |
57 | INITIALIZE_PASS_BEGIN(X86SuppressAPXForRelocationPass, DEBUG_TYPE, |
58 | "X86 Suppress APX features for relocation" , false, false) |
59 | INITIALIZE_PASS_END(X86SuppressAPXForRelocationPass, DEBUG_TYPE, |
60 | "X86 Suppress APX features for relocation" , false, false) |
61 | |
62 | FunctionPass *llvm::createX86SuppressAPXForRelocationPass() { |
63 | return new X86SuppressAPXForRelocationPass(); |
64 | } |
65 | |
66 | static void suppressEGPRRegClass(MachineRegisterInfo *MRI, MachineInstr &MI, |
67 | const X86Subtarget &ST, unsigned int OpNum) { |
68 | Register Reg = MI.getOperand(i: OpNum).getReg(); |
69 | if (!Reg.isVirtual()) { |
70 | assert(!X86II::isApxExtendedReg(Reg) && "APX EGPR is used unexpectedly." ); |
71 | return; |
72 | } |
73 | const TargetRegisterClass *RC = MRI->getRegClass(Reg); |
74 | const X86RegisterInfo *RI = ST.getRegisterInfo(); |
75 | const TargetRegisterClass *NewRC = RI->constrainRegClassToNonRex2(RC); |
76 | MRI->setRegClass(Reg, RC: NewRC); |
77 | } |
78 | |
79 | // Suppress EGPR in operand 0 of uses to avoid APX relocation types emitted. The |
80 | // register in operand 0 of instruction with relocation may be replaced with |
81 | // operand 0 of uses which may be EGPR. That may lead to emit APX relocation |
82 | // types which breaks the backward compatibility with builtin linkers on |
83 | // existing OS. For example, the register in operand 0 of instruction with |
84 | // relocation is used in PHI instruction, and it may be replaced with operand 0 |
85 | // of PHI instruction after PHI elimination and Machine Copy Propagation pass. |
86 | static void suppressEGPRRegClassInRegAndUses(MachineRegisterInfo *MRI, |
87 | MachineInstr &MI, |
88 | const X86Subtarget &ST, |
89 | unsigned int OpNum) { |
90 | suppressEGPRRegClass(MRI, MI, ST, OpNum); |
91 | Register Reg = MI.getOperand(i: OpNum).getReg(); |
92 | for (MachineInstr &Use : MRI->use_instructions(Reg)) |
93 | if (Use.getOpcode() == X86::PHI) |
94 | suppressEGPRRegClass(MRI, MI&: Use, ST, OpNum: 0); |
95 | } |
96 | |
97 | static bool handleInstructionWithEGPR(MachineFunction &MF, |
98 | const X86Subtarget &ST) { |
99 | if (!ST.hasEGPR()) |
100 | return false; |
101 | |
102 | MachineRegisterInfo *MRI = &MF.getRegInfo(); |
103 | auto suppressEGPRInInstrWithReloc = [&](MachineInstr &MI, |
104 | ArrayRef<unsigned> OpNoArray) { |
105 | int MemOpNo = X86II::getMemoryOperandNo(TSFlags: MI.getDesc().TSFlags) + |
106 | X86II::getOperandBias(Desc: MI.getDesc()); |
107 | const MachineOperand &MO = MI.getOperand(i: X86::AddrDisp + MemOpNo); |
108 | if (MO.getTargetFlags() == X86II::MO_GOTTPOFF || |
109 | MO.getTargetFlags() == X86II::MO_GOTPCREL) { |
110 | LLVM_DEBUG(dbgs() << "Transform instruction with relocation type:\n " |
111 | << MI); |
112 | for (unsigned OpNo : OpNoArray) |
113 | suppressEGPRRegClassInRegAndUses(MRI, MI, ST, OpNum: OpNo); |
114 | LLVM_DEBUG(dbgs() << "to:\n " << MI << "\n" ); |
115 | } |
116 | }; |
117 | |
118 | for (MachineBasicBlock &MBB : MF) { |
119 | for (MachineInstr &MI : MBB) { |
120 | unsigned Opcode = MI.getOpcode(); |
121 | switch (Opcode) { |
122 | // For GOTPC32_TLSDESC, it's emitted with physical register (EAX/RAX) in |
123 | // X86AsmPrinter::LowerTlsAddr, and there is no corresponding target |
124 | // flag for it, so we don't need to handle LEA64r with TLSDESC and EGPR |
125 | // in this pass (before emitting assembly). |
126 | case X86::TEST32mr: |
127 | case X86::TEST64mr: { |
128 | suppressEGPRInInstrWithReloc(MI, {5}); |
129 | break; |
130 | } |
131 | case X86::CMP32rm: |
132 | case X86::CMP64rm: |
133 | case X86::MOV32rm: |
134 | case X86::MOV64rm: { |
135 | suppressEGPRInInstrWithReloc(MI, {0}); |
136 | break; |
137 | } |
138 | case X86::ADC32rm: |
139 | case X86::ADD32rm: |
140 | case X86::AND32rm: |
141 | case X86::OR32rm: |
142 | case X86::SBB32rm: |
143 | case X86::SUB32rm: |
144 | case X86::XOR32rm: |
145 | case X86::ADC64rm: |
146 | case X86::ADD64rm: |
147 | case X86::AND64rm: |
148 | case X86::OR64rm: |
149 | case X86::SBB64rm: |
150 | case X86::SUB64rm: |
151 | case X86::XOR64rm: { |
152 | suppressEGPRInInstrWithReloc(MI, {0, 1}); |
153 | break; |
154 | } |
155 | } |
156 | } |
157 | } |
158 | return true; |
159 | } |
160 | |
161 | static bool handleNDDOrNFInstructions(MachineFunction &MF, |
162 | const X86Subtarget &ST) { |
163 | if (!ST.hasNDD() && !ST.hasNF()) |
164 | return false; |
165 | |
166 | const X86InstrInfo *TII = ST.getInstrInfo(); |
167 | MachineRegisterInfo *MRI = &MF.getRegInfo(); |
168 | for (MachineBasicBlock &MBB : MF) { |
169 | for (MachineInstr &MI : llvm::make_early_inc_range(Range&: MBB)) { |
170 | unsigned Opcode = MI.getOpcode(); |
171 | switch (Opcode) { |
172 | case X86::ADD64rm_NF: |
173 | case X86::ADD64mr_NF_ND: |
174 | case X86::ADD64rm_NF_ND: { |
175 | int MemOpNo = X86II::getMemoryOperandNo(TSFlags: MI.getDesc().TSFlags) + |
176 | X86II::getOperandBias(Desc: MI.getDesc()); |
177 | const MachineOperand &MO = MI.getOperand(i: X86::AddrDisp + MemOpNo); |
178 | if (MO.getTargetFlags() == X86II::MO_GOTTPOFF) |
179 | llvm_unreachable("Unexpected NF instruction!" ); |
180 | break; |
181 | } |
182 | case X86::ADD64rm_ND: { |
183 | int MemOpNo = X86II::getMemoryOperandNo(TSFlags: MI.getDesc().TSFlags) + |
184 | X86II::getOperandBias(Desc: MI.getDesc()); |
185 | const MachineOperand &MO = MI.getOperand(i: X86::AddrDisp + MemOpNo); |
186 | if (MO.getTargetFlags() == X86II::MO_GOTTPOFF || |
187 | MO.getTargetFlags() == X86II::MO_GOTPCREL) { |
188 | LLVM_DEBUG(dbgs() << "Transform instruction with relocation type:\n " |
189 | << MI); |
190 | Register Reg = MRI->createVirtualRegister(RegClass: &X86::GR64_NOREX2RegClass); |
191 | [[maybe_unused]] MachineInstrBuilder CopyMIB = |
192 | BuildMI(BB&: MBB, I&: MI, MIMD: MI.getDebugLoc(), MCID: TII->get(Opcode: TargetOpcode::COPY), |
193 | DestReg: Reg) |
194 | .addReg(RegNo: MI.getOperand(i: 1).getReg()); |
195 | MI.getOperand(i: 1).setReg(Reg); |
196 | const MCInstrDesc &NewDesc = TII->get(Opcode: X86::ADD64rm); |
197 | MI.setDesc(NewDesc); |
198 | suppressEGPRRegClassInRegAndUses(MRI, MI, ST, OpNum: 0); |
199 | MI.tieOperands(DefIdx: 0, UseIdx: 1); |
200 | LLVM_DEBUG(dbgs() << "to:\n " << *CopyMIB << "\n" ); |
201 | LLVM_DEBUG(dbgs() << " " << MI << "\n" ); |
202 | } |
203 | break; |
204 | } |
205 | case X86::ADD64mr_ND: { |
206 | int MemRefBegin = X86II::getMemoryOperandNo(TSFlags: MI.getDesc().TSFlags); |
207 | const MachineOperand &MO = MI.getOperand(i: MemRefBegin + X86::AddrDisp); |
208 | if (MO.getTargetFlags() == X86II::MO_GOTTPOFF) { |
209 | LLVM_DEBUG(dbgs() << "Transform instruction with relocation type:\n " |
210 | << MI); |
211 | suppressEGPRRegClassInRegAndUses(MRI, MI, ST, OpNum: 0); |
212 | Register Reg = MRI->createVirtualRegister(RegClass: &X86::GR64_NOREX2RegClass); |
213 | [[maybe_unused]] MachineInstrBuilder CopyMIB = |
214 | BuildMI(BB&: MBB, I&: MI, MIMD: MI.getDebugLoc(), MCID: TII->get(Opcode: TargetOpcode::COPY), |
215 | DestReg: Reg) |
216 | .addReg(RegNo: MI.getOperand(i: 6).getReg()); |
217 | MachineInstrBuilder NewMIB = |
218 | BuildMI(BB&: MBB, I&: MI, MIMD: MI.getDebugLoc(), MCID: TII->get(Opcode: X86::ADD64rm), |
219 | DestReg: MI.getOperand(i: 0).getReg()) |
220 | .addReg(RegNo: Reg) |
221 | .addReg(RegNo: MI.getOperand(i: 1).getReg()) |
222 | .addImm(Val: MI.getOperand(i: 2).getImm()) |
223 | .addReg(RegNo: MI.getOperand(i: 3).getReg()) |
224 | .add(MO: MI.getOperand(i: 4)) |
225 | .addReg(RegNo: MI.getOperand(i: 5).getReg()); |
226 | MachineOperand *FlagDef = |
227 | MI.findRegisterDefOperand(Reg: X86::EFLAGS, /*TRI=*/nullptr); |
228 | if (FlagDef && FlagDef->isDead()) { |
229 | MachineOperand *NewFlagDef = |
230 | NewMIB->findRegisterDefOperand(Reg: X86::EFLAGS, /*TRI=*/nullptr); |
231 | if (NewFlagDef) |
232 | NewFlagDef->setIsDead(); |
233 | } |
234 | MI.eraseFromParent(); |
235 | LLVM_DEBUG(dbgs() << "to:\n " << *CopyMIB << "\n" ); |
236 | LLVM_DEBUG(dbgs() << " " << *NewMIB << "\n" ); |
237 | } |
238 | break; |
239 | } |
240 | } |
241 | } |
242 | } |
243 | return true; |
244 | } |
245 | |
246 | bool X86SuppressAPXForRelocationPass::runOnMachineFunction( |
247 | MachineFunction &MF) { |
248 | if (X86EnableAPXForRelocation) |
249 | return false; |
250 | const X86Subtarget &ST = MF.getSubtarget<X86Subtarget>(); |
251 | bool Changed = handleInstructionWithEGPR(MF, ST); |
252 | Changed |= handleNDDOrNFInstructions(MF, ST); |
253 | |
254 | return Changed; |
255 | } |
256 | |