| //===-- WebAssemblyLowerBrUnless.cpp - Lower br_unless --------------------===// | |
| // | |
| // The LLVM Compiler Infrastructure | |
| // | |
| // This file is distributed under the University of Illinois Open Source | |
| // License. See LICENSE.TXT for details. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| /// | |
| /// \file | |
| /// This file lowers br_unless into br_if with an inverted condition. | |
| /// | |
| /// br_unless is not currently in the spec, but it's very convenient for LLVM | |
| /// to use. This pass allows LLVM to use it, for now. | |
| /// | |
| //===----------------------------------------------------------------------===// | |
| #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" | |
| #include "WebAssembly.h" | |
| #include "WebAssemblyMachineFunctionInfo.h" | |
| #include "WebAssemblySubtarget.h" | |
| #include "llvm/CodeGen/MachineFunctionPass.h" | |
| #include "llvm/CodeGen/MachineInstrBuilder.h" | |
| #include "llvm/Support/Debug.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| using namespace llvm; | |
| #define DEBUG_TYPE "wasm-lower-br_unless" | |
| namespace { | |
| class WebAssemblyLowerBrUnless final : public MachineFunctionPass { | |
| StringRef getPassName() const override { | |
| return "WebAssembly Lower br_unless"; | |
| } | |
| void getAnalysisUsage(AnalysisUsage &AU) const override { | |
| AU.setPreservesCFG(); | |
| MachineFunctionPass::getAnalysisUsage(AU); | |
| } | |
| bool runOnMachineFunction(MachineFunction &MF) override; | |
| public: | |
| static char ID; // Pass identification, replacement for typeid | |
| WebAssemblyLowerBrUnless() : MachineFunctionPass(ID) {} | |
| }; | |
| } // end anonymous namespace | |
| char WebAssemblyLowerBrUnless::ID = 0; | |
| INITIALIZE_PASS(WebAssemblyLowerBrUnless, DEBUG_TYPE, | |
| "Lowers br_unless into inverted br_if", false, false) | |
| FunctionPass *llvm::createWebAssemblyLowerBrUnless() { | |
| return new WebAssemblyLowerBrUnless(); | |
| } | |
| bool WebAssemblyLowerBrUnless::runOnMachineFunction(MachineFunction &MF) { | |
| LLVM_DEBUG(dbgs() << "********** Lowering br_unless **********\n" | |
| "********** Function: " | |
| << MF.getName() << '\n'); | |
| auto &MFI = *MF.getInfo<WebAssemblyFunctionInfo>(); | |
| const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); | |
| auto &MRI = MF.getRegInfo(); | |
| for (auto &MBB : MF) { | |
| for (auto MII = MBB.begin(); MII != MBB.end();) { | |
| MachineInstr *MI = &*MII++; | |
| if (MI->getOpcode() != WebAssembly::BR_UNLESS) | |
| continue; | |
| unsigned Cond = MI->getOperand(1).getReg(); | |
| bool Inverted = false; | |
| // Attempt to invert the condition in place. | |
| if (MFI.isVRegStackified(Cond)) { | |
| assert(MRI.hasOneDef(Cond)); | |
| MachineInstr *Def = MRI.getVRegDef(Cond); | |
| switch (Def->getOpcode()) { | |
| using namespace WebAssembly; | |
| case EQ_I32: Def->setDesc(TII.get(NE_I32)); Inverted = true; break; | |
| case NE_I32: Def->setDesc(TII.get(EQ_I32)); Inverted = true; break; | |
| case GT_S_I32: Def->setDesc(TII.get(LE_S_I32)); Inverted = true; break; | |
| case GE_S_I32: Def->setDesc(TII.get(LT_S_I32)); Inverted = true; break; | |
| case LT_S_I32: Def->setDesc(TII.get(GE_S_I32)); Inverted = true; break; | |
| case LE_S_I32: Def->setDesc(TII.get(GT_S_I32)); Inverted = true; break; | |
| case GT_U_I32: Def->setDesc(TII.get(LE_U_I32)); Inverted = true; break; | |
| case GE_U_I32: Def->setDesc(TII.get(LT_U_I32)); Inverted = true; break; | |
| case LT_U_I32: Def->setDesc(TII.get(GE_U_I32)); Inverted = true; break; | |
| case LE_U_I32: Def->setDesc(TII.get(GT_U_I32)); Inverted = true; break; | |
| case EQ_I64: Def->setDesc(TII.get(NE_I64)); Inverted = true; break; | |
| case NE_I64: Def->setDesc(TII.get(EQ_I64)); Inverted = true; break; | |
| case GT_S_I64: Def->setDesc(TII.get(LE_S_I64)); Inverted = true; break; | |
| case GE_S_I64: Def->setDesc(TII.get(LT_S_I64)); Inverted = true; break; | |
| case LT_S_I64: Def->setDesc(TII.get(GE_S_I64)); Inverted = true; break; | |
| case LE_S_I64: Def->setDesc(TII.get(GT_S_I64)); Inverted = true; break; | |
| case GT_U_I64: Def->setDesc(TII.get(LE_U_I64)); Inverted = true; break; | |
| case GE_U_I64: Def->setDesc(TII.get(LT_U_I64)); Inverted = true; break; | |
| case LT_U_I64: Def->setDesc(TII.get(GE_U_I64)); Inverted = true; break; | |
| case LE_U_I64: Def->setDesc(TII.get(GT_U_I64)); Inverted = true; break; | |
| case EQ_F32: Def->setDesc(TII.get(NE_F32)); Inverted = true; break; | |
| case NE_F32: Def->setDesc(TII.get(EQ_F32)); Inverted = true; break; | |
| case EQ_F64: Def->setDesc(TII.get(NE_F64)); Inverted = true; break; | |
| case NE_F64: Def->setDesc(TII.get(EQ_F64)); Inverted = true; break; | |
| case EQZ_I32: { | |
| // Invert an eqz by replacing it with its operand. | |
| Cond = Def->getOperand(1).getReg(); | |
| Def->eraseFromParent(); | |
| Inverted = true; | |
| break; | |
| } | |
| default: break; | |
| } | |
| } | |
| // If we weren't able to invert the condition in place. Insert an | |
| // instruction to invert it. | |
| if (!Inverted) { | |
| unsigned Tmp = MRI.createVirtualRegister(&WebAssembly::I32RegClass); | |
| BuildMI(MBB, MI, MI->getDebugLoc(), TII.get(WebAssembly::EQZ_I32), Tmp) | |
| .addReg(Cond); | |
| MFI.stackifyVReg(Tmp); | |
| Cond = Tmp; | |
| Inverted = true; | |
| } | |
| // The br_unless condition has now been inverted. Insert a br_if and | |
| // delete the br_unless. | |
| assert(Inverted); | |
| BuildMI(MBB, MI, MI->getDebugLoc(), TII.get(WebAssembly::BR_IF)) | |
| .add(MI->getOperand(0)) | |
| .addReg(Cond); | |
| MBB.erase(MI); | |
| } | |
| } | |
| return true; | |
| } |