1//===-- WebAssemblyRegisterInfo.cpp - WebAssembly Register Information ----===//
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 contains the WebAssembly implementation of the
11/// TargetRegisterInfo class.
12///
13//===----------------------------------------------------------------------===//
14
15#include "WebAssemblyRegisterInfo.h"
16#include "GISel/WebAssemblyRegisterBankInfo.h"
17#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
18#include "WebAssemblyFrameLowering.h"
19#include "WebAssemblyInstrInfo.h"
20#include "WebAssemblyMachineFunctionInfo.h"
21#include "WebAssemblySubtarget.h"
22#include "llvm/CodeGen/MachineFrameInfo.h"
23#include "llvm/CodeGen/MachineInstrBuilder.h"
24#include "llvm/CodeGen/MachineRegisterInfo.h"
25#include "llvm/CodeGen/TargetFrameLowering.h"
26#include "llvm/Target/TargetOptions.h"
27using namespace llvm;
28
29#define DEBUG_TYPE "wasm-reg-info"
30
31#define GET_REGINFO_TARGET_DESC
32#include "WebAssemblyGenRegisterInfo.inc"
33
34WebAssemblyRegisterInfo::WebAssemblyRegisterInfo(const Triple &TT)
35 : WebAssemblyGenRegisterInfo(0), TT(TT) {}
36
37const MCPhysReg *
38WebAssemblyRegisterInfo::getCalleeSavedRegs(const MachineFunction *) const {
39 static const MCPhysReg CalleeSavedRegs[] = {0};
40 return CalleeSavedRegs;
41}
42
43BitVector
44WebAssemblyRegisterInfo::getReservedRegs(const MachineFunction & /*MF*/) const {
45 BitVector Reserved(getNumRegs());
46 for (auto Reg : {WebAssembly::SP32, WebAssembly::SP64, WebAssembly::FP32,
47 WebAssembly::FP64})
48 Reserved.set(Reg);
49 return Reserved;
50}
51
52bool WebAssemblyRegisterInfo::eliminateFrameIndex(
53 MachineBasicBlock::iterator II, int SPAdj, unsigned FIOperandNum,
54 RegScavenger * /*RS*/) const {
55 assert(SPAdj == 0);
56 MachineInstr &MI = *II;
57
58 MachineBasicBlock &MBB = *MI.getParent();
59 MachineFunction &MF = *MBB.getParent();
60 MachineRegisterInfo &MRI = MF.getRegInfo();
61 int FrameIndex = MI.getOperand(i: FIOperandNum).getIndex();
62 const MachineFrameInfo &MFI = MF.getFrameInfo();
63 int64_t FrameOffset = MFI.getStackSize() + MFI.getObjectOffset(ObjectIdx: FrameIndex);
64
65 assert(MFI.getObjectSize(FrameIndex) != 0 &&
66 "We assume that variable-sized objects have already been lowered, "
67 "and don't use FrameIndex operands.");
68 Register FrameRegister = getFrameRegister(MF);
69
70 // If this is the address operand of a load or store, make it relative to SP
71 // and fold the frame offset directly in.
72 unsigned AddrOperandNum = WebAssembly::getNamedOperandIdx(
73 Opcode: MI.getOpcode(), Name: WebAssembly::OpName::addr);
74 if (AddrOperandNum == FIOperandNum) {
75 unsigned OffsetOperandNum = WebAssembly::getNamedOperandIdx(
76 Opcode: MI.getOpcode(), Name: WebAssembly::OpName::off);
77 assert(FrameOffset >= 0 && MI.getOperand(OffsetOperandNum).getImm() >= 0);
78 int64_t Offset = MI.getOperand(i: OffsetOperandNum).getImm() + FrameOffset;
79
80 if (static_cast<uint64_t>(Offset) <= std::numeric_limits<uint32_t>::max()) {
81 MI.getOperand(i: OffsetOperandNum).setImm(Offset);
82 MI.getOperand(i: FIOperandNum)
83 .ChangeToRegister(Reg: FrameRegister, /*isDef=*/false);
84 return false;
85 }
86 }
87
88 // If this is an address being added to a constant, fold the frame offset
89 // into the constant.
90 if (MI.getOpcode() == WebAssemblyFrameLowering::getOpcAdd(MF)) {
91 MachineOperand &OtherMO = MI.getOperand(i: 3 - FIOperandNum);
92 if (OtherMO.isReg()) {
93 Register OtherMOReg = OtherMO.getReg();
94 if (OtherMOReg.isVirtual()) {
95 MachineInstr *Def = MF.getRegInfo().getUniqueVRegDef(Reg: OtherMOReg);
96 // TODO: For now we just opportunistically do this in the case where
97 // the CONST_I32/64 happens to have exactly one def and one use. We
98 // should generalize this to optimize in more cases.
99 if (Def && Def->getOpcode() ==
100 WebAssemblyFrameLowering::getOpcConst(MF) &&
101 MRI.hasOneNonDBGUse(RegNo: Def->getOperand(i: 0).getReg())) {
102 MachineOperand &ImmMO = Def->getOperand(i: 1);
103 if (ImmMO.isImm()) {
104 ImmMO.setImm(ImmMO.getImm() + uint32_t(FrameOffset));
105 MI.getOperand(i: FIOperandNum)
106 .ChangeToRegister(Reg: FrameRegister, /*isDef=*/false);
107 return false;
108 }
109 }
110 }
111 }
112 }
113
114 // Otherwise create an i32/64.add SP, offset and make it the operand.
115 const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
116
117 unsigned FIRegOperand = FrameRegister;
118 if (FrameOffset) {
119 // Create i32/64.add SP, offset and make it the operand.
120 const TargetRegisterClass *PtrRC =
121 MRI.getTargetRegisterInfo()->getPointerRegClass();
122 Register OffsetOp = MRI.createVirtualRegister(RegClass: PtrRC);
123 BuildMI(BB&: MBB, I&: *II, MIMD: II->getDebugLoc(),
124 MCID: TII->get(Opcode: WebAssemblyFrameLowering::getOpcConst(MF)),
125 DestReg: OffsetOp)
126 .addImm(Val: FrameOffset);
127 FIRegOperand = MRI.createVirtualRegister(RegClass: PtrRC);
128 BuildMI(BB&: MBB, I&: *II, MIMD: II->getDebugLoc(),
129 MCID: TII->get(Opcode: WebAssemblyFrameLowering::getOpcAdd(MF)),
130 DestReg: FIRegOperand)
131 .addReg(RegNo: FrameRegister)
132 .addReg(RegNo: OffsetOp);
133 }
134 MI.getOperand(i: FIOperandNum).ChangeToRegister(Reg: FIRegOperand, /*isDef=*/false);
135 return false;
136}
137
138Register
139WebAssemblyRegisterInfo::getFrameRegister(const MachineFunction &MF) const {
140 // If the PReg has been replaced by a VReg, return that.
141 const auto &MFI = MF.getInfo<WebAssemblyFunctionInfo>();
142 if (MFI->isFrameBaseVirtual())
143 return MFI->getFrameBaseVreg();
144 static const unsigned Regs[2][2] = {
145 /* !isArch64Bit isArch64Bit */
146 /* !hasFP */ {WebAssembly::SP32, WebAssembly::SP64},
147 /* hasFP */ {WebAssembly::FP32, WebAssembly::FP64}};
148 const WebAssemblyFrameLowering *TFI = getFrameLowering(MF);
149 return Regs[TFI->hasFP(MF)][TT.isArch64Bit()];
150}
151
152const TargetRegisterClass *
153WebAssemblyRegisterInfo::getPointerRegClass(unsigned Kind) const {
154 assert(Kind == 0 && "Only one kind of pointer on WebAssembly");
155 return TT.getArch() == Triple::wasm64 ? &WebAssembly::I64RegClass
156 : &WebAssembly::I32RegClass;
157}
158
159static const TargetRegisterClass &getRegClassForBank(const RegisterBank &RB) {
160 switch (RB.getID()) {
161 case WebAssembly::I32RegBankID:
162 return WebAssembly::I32RegClass;
163 case WebAssembly::I64RegBankID:
164 return WebAssembly::I64RegClass;
165 case WebAssembly::F32RegBankID:
166 return WebAssembly::F32RegClass;
167 case WebAssembly::F64RegBankID:
168 return WebAssembly::F64RegClass;
169 case WebAssembly::EXNREFRegBankID:
170 return WebAssembly::EXNREFRegClass;
171 case WebAssembly::EXTERNREFRegBankID:
172 return WebAssembly::EXTERNREFRegClass;
173 case WebAssembly::FUNCREFRegBankID:
174 return WebAssembly::FUNCREFRegClass;
175 case WebAssembly::V128RegBankID:
176 return WebAssembly::V128RegClass;
177 default:
178 llvm_unreachable("Found unexpected RegisterBank in `getRegClassForBank`");
179 }
180}
181
182const TargetRegisterClass *
183WebAssemblyRegisterInfo::getConstrainedRegClassForOperand(
184 const MachineOperand &MO, const MachineRegisterInfo &MRI) const {
185 assert(MO.isReg());
186
187 const RegClassOrRegBank &RegClassOrBank =
188 MRI.getRegClassOrRegBank(Reg: MO.getReg());
189
190 if (RegClassOrBank.isNull())
191 return nullptr;
192
193 const TargetRegisterClass *DefRC =
194 dyn_cast<const TargetRegisterClass *>(Val: RegClassOrBank);
195
196 if (!DefRC) {
197 const RegisterBank &RB = *cast<const RegisterBank *>(Val: RegClassOrBank);
198 DefRC = &getRegClassForBank(RB);
199 }
200
201 return DefRC;
202}
203