1292915Sdim//===-- WebAssemblyLowerBrUnless.cpp - Lower br_unless --------------------===//
2292915Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6292915Sdim//
7292915Sdim//===----------------------------------------------------------------------===//
8292915Sdim///
9292915Sdim/// \file
10341825Sdim/// This file lowers br_unless into br_if with an inverted condition.
11292915Sdim///
12292915Sdim/// br_unless is not currently in the spec, but it's very convenient for LLVM
13292915Sdim/// to use. This pass allows LLVM to use it, for now.
14292915Sdim///
15292915Sdim//===----------------------------------------------------------------------===//
16292915Sdim
17321369Sdim#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
18292915Sdim#include "WebAssembly.h"
19292915Sdim#include "WebAssemblyMachineFunctionInfo.h"
20292915Sdim#include "WebAssemblySubtarget.h"
21292915Sdim#include "llvm/CodeGen/MachineFunctionPass.h"
22292915Sdim#include "llvm/CodeGen/MachineInstrBuilder.h"
23292915Sdim#include "llvm/Support/Debug.h"
24292915Sdim#include "llvm/Support/raw_ostream.h"
25292915Sdimusing namespace llvm;
26292915Sdim
27292915Sdim#define DEBUG_TYPE "wasm-lower-br_unless"
28292915Sdim
29292915Sdimnamespace {
30292915Sdimclass WebAssemblyLowerBrUnless final : public MachineFunctionPass {
31314564Sdim  StringRef getPassName() const override {
32292915Sdim    return "WebAssembly Lower br_unless";
33292915Sdim  }
34292915Sdim
35292915Sdim  void getAnalysisUsage(AnalysisUsage &AU) const override {
36292915Sdim    AU.setPreservesCFG();
37292915Sdim    MachineFunctionPass::getAnalysisUsage(AU);
38292915Sdim  }
39292915Sdim
40292915Sdim  bool runOnMachineFunction(MachineFunction &MF) override;
41292915Sdim
42292915Sdimpublic:
43292915Sdim  static char ID; // Pass identification, replacement for typeid
44292915Sdim  WebAssemblyLowerBrUnless() : MachineFunctionPass(ID) {}
45292915Sdim};
46292915Sdim} // end anonymous namespace
47292915Sdim
48292915Sdimchar WebAssemblyLowerBrUnless::ID = 0;
49341825SdimINITIALIZE_PASS(WebAssemblyLowerBrUnless, DEBUG_TYPE,
50341825Sdim                "Lowers br_unless into inverted br_if", false, false)
51341825Sdim
52292915SdimFunctionPass *llvm::createWebAssemblyLowerBrUnless() {
53292915Sdim  return new WebAssemblyLowerBrUnless();
54292915Sdim}
55292915Sdim
56292915Sdimbool WebAssemblyLowerBrUnless::runOnMachineFunction(MachineFunction &MF) {
57341825Sdim  LLVM_DEBUG(dbgs() << "********** Lowering br_unless **********\n"
58341825Sdim                       "********** Function: "
59341825Sdim                    << MF.getName() << '\n');
60292915Sdim
61292915Sdim  auto &MFI = *MF.getInfo<WebAssemblyFunctionInfo>();
62292915Sdim  const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
63292915Sdim  auto &MRI = MF.getRegInfo();
64292915Sdim
65292915Sdim  for (auto &MBB : MF) {
66309124Sdim    for (auto MII = MBB.begin(); MII != MBB.end();) {
67292915Sdim      MachineInstr *MI = &*MII++;
68292915Sdim      if (MI->getOpcode() != WebAssembly::BR_UNLESS)
69292915Sdim        continue;
70292915Sdim
71360784Sdim      Register Cond = MI->getOperand(1).getReg();
72292915Sdim      bool Inverted = false;
73292915Sdim
74292915Sdim      // Attempt to invert the condition in place.
75292915Sdim      if (MFI.isVRegStackified(Cond)) {
76292915Sdim        assert(MRI.hasOneDef(Cond));
77292915Sdim        MachineInstr *Def = MRI.getVRegDef(Cond);
78292915Sdim        switch (Def->getOpcode()) {
79309124Sdim          using namespace WebAssembly;
80344779Sdim        case EQ_I32:
81344779Sdim          Def->setDesc(TII.get(NE_I32));
82344779Sdim          Inverted = true;
83344779Sdim          break;
84344779Sdim        case NE_I32:
85344779Sdim          Def->setDesc(TII.get(EQ_I32));
86344779Sdim          Inverted = true;
87344779Sdim          break;
88344779Sdim        case GT_S_I32:
89344779Sdim          Def->setDesc(TII.get(LE_S_I32));
90344779Sdim          Inverted = true;
91344779Sdim          break;
92344779Sdim        case GE_S_I32:
93344779Sdim          Def->setDesc(TII.get(LT_S_I32));
94344779Sdim          Inverted = true;
95344779Sdim          break;
96344779Sdim        case LT_S_I32:
97344779Sdim          Def->setDesc(TII.get(GE_S_I32));
98344779Sdim          Inverted = true;
99344779Sdim          break;
100344779Sdim        case LE_S_I32:
101344779Sdim          Def->setDesc(TII.get(GT_S_I32));
102344779Sdim          Inverted = true;
103344779Sdim          break;
104344779Sdim        case GT_U_I32:
105344779Sdim          Def->setDesc(TII.get(LE_U_I32));
106344779Sdim          Inverted = true;
107344779Sdim          break;
108344779Sdim        case GE_U_I32:
109344779Sdim          Def->setDesc(TII.get(LT_U_I32));
110344779Sdim          Inverted = true;
111344779Sdim          break;
112344779Sdim        case LT_U_I32:
113344779Sdim          Def->setDesc(TII.get(GE_U_I32));
114344779Sdim          Inverted = true;
115344779Sdim          break;
116344779Sdim        case LE_U_I32:
117344779Sdim          Def->setDesc(TII.get(GT_U_I32));
118344779Sdim          Inverted = true;
119344779Sdim          break;
120344779Sdim        case EQ_I64:
121344779Sdim          Def->setDesc(TII.get(NE_I64));
122344779Sdim          Inverted = true;
123344779Sdim          break;
124344779Sdim        case NE_I64:
125344779Sdim          Def->setDesc(TII.get(EQ_I64));
126344779Sdim          Inverted = true;
127344779Sdim          break;
128344779Sdim        case GT_S_I64:
129344779Sdim          Def->setDesc(TII.get(LE_S_I64));
130344779Sdim          Inverted = true;
131344779Sdim          break;
132344779Sdim        case GE_S_I64:
133344779Sdim          Def->setDesc(TII.get(LT_S_I64));
134344779Sdim          Inverted = true;
135344779Sdim          break;
136344779Sdim        case LT_S_I64:
137344779Sdim          Def->setDesc(TII.get(GE_S_I64));
138344779Sdim          Inverted = true;
139344779Sdim          break;
140344779Sdim        case LE_S_I64:
141344779Sdim          Def->setDesc(TII.get(GT_S_I64));
142344779Sdim          Inverted = true;
143344779Sdim          break;
144344779Sdim        case GT_U_I64:
145344779Sdim          Def->setDesc(TII.get(LE_U_I64));
146344779Sdim          Inverted = true;
147344779Sdim          break;
148344779Sdim        case GE_U_I64:
149344779Sdim          Def->setDesc(TII.get(LT_U_I64));
150344779Sdim          Inverted = true;
151344779Sdim          break;
152344779Sdim        case LT_U_I64:
153344779Sdim          Def->setDesc(TII.get(GE_U_I64));
154344779Sdim          Inverted = true;
155344779Sdim          break;
156344779Sdim        case LE_U_I64:
157344779Sdim          Def->setDesc(TII.get(GT_U_I64));
158344779Sdim          Inverted = true;
159344779Sdim          break;
160344779Sdim        case EQ_F32:
161344779Sdim          Def->setDesc(TII.get(NE_F32));
162344779Sdim          Inverted = true;
163344779Sdim          break;
164344779Sdim        case NE_F32:
165344779Sdim          Def->setDesc(TII.get(EQ_F32));
166344779Sdim          Inverted = true;
167344779Sdim          break;
168344779Sdim        case EQ_F64:
169344779Sdim          Def->setDesc(TII.get(NE_F64));
170344779Sdim          Inverted = true;
171344779Sdim          break;
172344779Sdim        case NE_F64:
173344779Sdim          Def->setDesc(TII.get(EQ_F64));
174344779Sdim          Inverted = true;
175344779Sdim          break;
176327952Sdim        case EQZ_I32: {
177327952Sdim          // Invert an eqz by replacing it with its operand.
178327952Sdim          Cond = Def->getOperand(1).getReg();
179327952Sdim          Def->eraseFromParent();
180327952Sdim          Inverted = true;
181327952Sdim          break;
182327952Sdim        }
183344779Sdim        default:
184344779Sdim          break;
185292915Sdim        }
186292915Sdim      }
187292915Sdim
188292915Sdim      // If we weren't able to invert the condition in place. Insert an
189314564Sdim      // instruction to invert it.
190292915Sdim      if (!Inverted) {
191360784Sdim        Register Tmp = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
192309124Sdim        BuildMI(MBB, MI, MI->getDebugLoc(), TII.get(WebAssembly::EQZ_I32), Tmp)
193309124Sdim            .addReg(Cond);
194314564Sdim        MFI.stackifyVReg(Tmp);
195292915Sdim        Cond = Tmp;
196292915Sdim        Inverted = true;
197292915Sdim      }
198292915Sdim
199292915Sdim      // The br_unless condition has now been inverted. Insert a br_if and
200292915Sdim      // delete the br_unless.
201292915Sdim      assert(Inverted);
202292915Sdim      BuildMI(MBB, MI, MI->getDebugLoc(), TII.get(WebAssembly::BR_IF))
203321369Sdim          .add(MI->getOperand(0))
204309124Sdim          .addReg(Cond);
205292915Sdim      MBB.erase(MI);
206292915Sdim    }
207292915Sdim  }
208292915Sdim
209292915Sdim  return true;
210292915Sdim}
211