1//===-- WebAssemblyLowerBrUnless.cpp - Lower br_unless --------------------===//
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 lowers br_unless into br_if with an inverted condition.
11///
12/// br_unless is not currently in the spec, but it's very convenient for LLVM
13/// to use. This pass allows LLVM to use it, for now.
14///
15//===----------------------------------------------------------------------===//
16
17#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
18#include "WebAssembly.h"
19#include "WebAssemblyMachineFunctionInfo.h"
20#include "WebAssemblySubtarget.h"
21#include "llvm/CodeGen/MachineFunctionAnalysisManager.h"
22#include "llvm/CodeGen/MachineFunctionPass.h"
23#include "llvm/CodeGen/MachineInstrBuilder.h"
24#include "llvm/CodeGen/MachinePassManager.h"
25#include "llvm/IR/Analysis.h"
26#include "llvm/Support/Debug.h"
27#include "llvm/Support/raw_ostream.h"
28using namespace llvm;
29
30#define DEBUG_TYPE "wasm-lower-br_unless"
31
32namespace {
33class WebAssemblyLowerBrUnlessLegacy final : public MachineFunctionPass {
34 StringRef getPassName() const override {
35 return "WebAssembly Lower br_unless";
36 }
37
38 void getAnalysisUsage(AnalysisUsage &AU) const override {
39 AU.setPreservesCFG();
40 MachineFunctionPass::getAnalysisUsage(AU);
41 }
42
43 bool runOnMachineFunction(MachineFunction &MF) override;
44
45public:
46 static char ID; // Pass identification, replacement for typeid
47 WebAssemblyLowerBrUnlessLegacy() : MachineFunctionPass(ID) {}
48};
49} // end anonymous namespace
50
51char WebAssemblyLowerBrUnlessLegacy::ID = 0;
52INITIALIZE_PASS(WebAssemblyLowerBrUnlessLegacy, DEBUG_TYPE,
53 "Lowers br_unless into inverted br_if", false, false)
54
55FunctionPass *llvm::createWebAssemblyLowerBrUnlessLegacyPass() {
56 return new WebAssemblyLowerBrUnlessLegacy();
57}
58
59static bool lowerBrUnless(MachineFunction &MF) {
60 LLVM_DEBUG(dbgs() << "********** Lowering br_unless **********\n"
61 "********** Function: "
62 << MF.getName() << '\n');
63
64 auto &MFI = *MF.getInfo<WebAssemblyFunctionInfo>();
65 const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
66 auto &MRI = MF.getRegInfo();
67
68 for (auto &MBB : MF) {
69 for (MachineInstr &MI : llvm::make_early_inc_range(Range&: MBB)) {
70 if (MI.getOpcode() != WebAssembly::BR_UNLESS)
71 continue;
72
73 Register Cond = MI.getOperand(i: 1).getReg();
74 bool Inverted = false;
75
76 // Attempt to invert the condition in place.
77 if (MFI.isVRegStackified(VReg: Cond)) {
78 assert(MRI.hasOneDef(Cond));
79 MachineInstr *Def = MRI.getVRegDef(Reg: Cond);
80 switch (Def->getOpcode()) {
81 using namespace WebAssembly;
82 case EQ_I32:
83 Def->setDesc(TII.get(Opcode: NE_I32));
84 Inverted = true;
85 break;
86 case NE_I32:
87 Def->setDesc(TII.get(Opcode: EQ_I32));
88 Inverted = true;
89 break;
90 case GT_S_I32:
91 Def->setDesc(TII.get(Opcode: LE_S_I32));
92 Inverted = true;
93 break;
94 case GE_S_I32:
95 Def->setDesc(TII.get(Opcode: LT_S_I32));
96 Inverted = true;
97 break;
98 case LT_S_I32:
99 Def->setDesc(TII.get(Opcode: GE_S_I32));
100 Inverted = true;
101 break;
102 case LE_S_I32:
103 Def->setDesc(TII.get(Opcode: GT_S_I32));
104 Inverted = true;
105 break;
106 case GT_U_I32:
107 Def->setDesc(TII.get(Opcode: LE_U_I32));
108 Inverted = true;
109 break;
110 case GE_U_I32:
111 Def->setDesc(TII.get(Opcode: LT_U_I32));
112 Inverted = true;
113 break;
114 case LT_U_I32:
115 Def->setDesc(TII.get(Opcode: GE_U_I32));
116 Inverted = true;
117 break;
118 case LE_U_I32:
119 Def->setDesc(TII.get(Opcode: GT_U_I32));
120 Inverted = true;
121 break;
122 case EQ_I64:
123 Def->setDesc(TII.get(Opcode: NE_I64));
124 Inverted = true;
125 break;
126 case NE_I64:
127 Def->setDesc(TII.get(Opcode: EQ_I64));
128 Inverted = true;
129 break;
130 case GT_S_I64:
131 Def->setDesc(TII.get(Opcode: LE_S_I64));
132 Inverted = true;
133 break;
134 case GE_S_I64:
135 Def->setDesc(TII.get(Opcode: LT_S_I64));
136 Inverted = true;
137 break;
138 case LT_S_I64:
139 Def->setDesc(TII.get(Opcode: GE_S_I64));
140 Inverted = true;
141 break;
142 case LE_S_I64:
143 Def->setDesc(TII.get(Opcode: GT_S_I64));
144 Inverted = true;
145 break;
146 case GT_U_I64:
147 Def->setDesc(TII.get(Opcode: LE_U_I64));
148 Inverted = true;
149 break;
150 case GE_U_I64:
151 Def->setDesc(TII.get(Opcode: LT_U_I64));
152 Inverted = true;
153 break;
154 case LT_U_I64:
155 Def->setDesc(TII.get(Opcode: GE_U_I64));
156 Inverted = true;
157 break;
158 case LE_U_I64:
159 Def->setDesc(TII.get(Opcode: GT_U_I64));
160 Inverted = true;
161 break;
162 case EQ_F32:
163 Def->setDesc(TII.get(Opcode: NE_F32));
164 Inverted = true;
165 break;
166 case NE_F32:
167 Def->setDesc(TII.get(Opcode: EQ_F32));
168 Inverted = true;
169 break;
170 case EQ_F64:
171 Def->setDesc(TII.get(Opcode: NE_F64));
172 Inverted = true;
173 break;
174 case NE_F64:
175 Def->setDesc(TII.get(Opcode: EQ_F64));
176 Inverted = true;
177 break;
178 case EQZ_I32: {
179 // Invert an eqz by replacing it with its operand.
180 Cond = Def->getOperand(i: 1).getReg();
181 Def->eraseFromParent();
182 Inverted = true;
183 break;
184 }
185 default:
186 break;
187 }
188 }
189
190 // If we weren't able to invert the condition in place. Insert an
191 // instruction to invert it.
192 if (!Inverted) {
193 Register Tmp = MRI.createVirtualRegister(RegClass: &WebAssembly::I32RegClass);
194 BuildMI(BB&: MBB, I: &MI, MIMD: MI.getDebugLoc(), MCID: TII.get(Opcode: WebAssembly::EQZ_I32), DestReg: Tmp)
195 .addReg(RegNo: Cond);
196 MFI.stackifyVReg(MRI, VReg: Tmp);
197 Cond = Tmp;
198 Inverted = true;
199 }
200
201 // The br_unless condition has now been inverted. Insert a br_if and
202 // delete the br_unless.
203 assert(Inverted);
204 BuildMI(BB&: MBB, I: &MI, MIMD: MI.getDebugLoc(), MCID: TII.get(Opcode: WebAssembly::BR_IF))
205 .add(MO: MI.getOperand(i: 0))
206 .addReg(RegNo: Cond);
207 MBB.erase(I: &MI);
208 }
209 }
210
211 return true;
212}
213
214bool WebAssemblyLowerBrUnlessLegacy::runOnMachineFunction(MachineFunction &MF) {
215 return lowerBrUnless(MF);
216}
217
218PreservedAnalyses
219WebAssemblyLowerBrUnlessPass::run(MachineFunction &MF,
220 MachineFunctionAnalysisManager &MFAM) {
221 return lowerBrUnless(MF) ? getMachineFunctionPassPreservedAnalyses()
222 .preserveSet<CFGAnalyses>()
223 : PreservedAnalyses::all();
224}
225