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