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