Path: blob/main/contrib/llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp
35266 views
//===-- WebAssemblyExplicitLocals.cpp - Make Locals Explicit --------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7///8/// \file9/// This file converts any remaining registers into WebAssembly locals.10///11/// After register stackification and register coloring, convert non-stackified12/// registers into locals, inserting explicit local.get and local.set13/// instructions.14///15//===----------------------------------------------------------------------===//1617#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"18#include "WebAssembly.h"19#include "WebAssemblyDebugValueManager.h"20#include "WebAssemblyMachineFunctionInfo.h"21#include "WebAssemblySubtarget.h"22#include "WebAssemblyUtilities.h"23#include "llvm/CodeGen/MachineBlockFrequencyInfo.h"24#include "llvm/CodeGen/MachineInstrBuilder.h"25#include "llvm/CodeGen/MachineRegisterInfo.h"26#include "llvm/CodeGen/Passes.h"27#include "llvm/Support/Debug.h"28#include "llvm/Support/raw_ostream.h"29using namespace llvm;3031#define DEBUG_TYPE "wasm-explicit-locals"3233namespace {34class WebAssemblyExplicitLocals final : public MachineFunctionPass {35StringRef getPassName() const override {36return "WebAssembly Explicit Locals";37}3839void getAnalysisUsage(AnalysisUsage &AU) const override {40AU.setPreservesCFG();41AU.addPreserved<MachineBlockFrequencyInfoWrapperPass>();42MachineFunctionPass::getAnalysisUsage(AU);43}4445bool runOnMachineFunction(MachineFunction &MF) override;4647public:48static char ID; // Pass identification, replacement for typeid49WebAssemblyExplicitLocals() : MachineFunctionPass(ID) {}50};51} // end anonymous namespace5253char WebAssemblyExplicitLocals::ID = 0;54INITIALIZE_PASS(WebAssemblyExplicitLocals, DEBUG_TYPE,55"Convert registers to WebAssembly locals", false, false)5657FunctionPass *llvm::createWebAssemblyExplicitLocals() {58return new WebAssemblyExplicitLocals();59}6061static void checkFrameBase(WebAssemblyFunctionInfo &MFI, unsigned Local,62unsigned Reg) {63// Mark a local for the frame base vreg.64if (MFI.isFrameBaseVirtual() && Reg == MFI.getFrameBaseVreg()) {65LLVM_DEBUG({66dbgs() << "Allocating local " << Local << "for VReg "67<< Register::virtReg2Index(Reg) << '\n';68});69MFI.setFrameBaseLocal(Local);70}71}7273/// Return a local id number for the given register, assigning it a new one74/// if it doesn't yet have one.75static unsigned getLocalId(DenseMap<unsigned, unsigned> &Reg2Local,76WebAssemblyFunctionInfo &MFI, unsigned &CurLocal,77unsigned Reg) {78auto P = Reg2Local.insert(std::make_pair(Reg, CurLocal));79if (P.second) {80checkFrameBase(MFI, CurLocal, Reg);81++CurLocal;82}83return P.first->second;84}8586/// Get the appropriate drop opcode for the given register class.87static unsigned getDropOpcode(const TargetRegisterClass *RC) {88if (RC == &WebAssembly::I32RegClass)89return WebAssembly::DROP_I32;90if (RC == &WebAssembly::I64RegClass)91return WebAssembly::DROP_I64;92if (RC == &WebAssembly::F32RegClass)93return WebAssembly::DROP_F32;94if (RC == &WebAssembly::F64RegClass)95return WebAssembly::DROP_F64;96if (RC == &WebAssembly::V128RegClass)97return WebAssembly::DROP_V128;98if (RC == &WebAssembly::FUNCREFRegClass)99return WebAssembly::DROP_FUNCREF;100if (RC == &WebAssembly::EXTERNREFRegClass)101return WebAssembly::DROP_EXTERNREF;102if (RC == &WebAssembly::EXNREFRegClass)103return WebAssembly::DROP_EXNREF;104llvm_unreachable("Unexpected register class");105}106107/// Get the appropriate local.get opcode for the given register class.108static unsigned getLocalGetOpcode(const TargetRegisterClass *RC) {109if (RC == &WebAssembly::I32RegClass)110return WebAssembly::LOCAL_GET_I32;111if (RC == &WebAssembly::I64RegClass)112return WebAssembly::LOCAL_GET_I64;113if (RC == &WebAssembly::F32RegClass)114return WebAssembly::LOCAL_GET_F32;115if (RC == &WebAssembly::F64RegClass)116return WebAssembly::LOCAL_GET_F64;117if (RC == &WebAssembly::V128RegClass)118return WebAssembly::LOCAL_GET_V128;119if (RC == &WebAssembly::FUNCREFRegClass)120return WebAssembly::LOCAL_GET_FUNCREF;121if (RC == &WebAssembly::EXTERNREFRegClass)122return WebAssembly::LOCAL_GET_EXTERNREF;123if (RC == &WebAssembly::EXNREFRegClass)124return WebAssembly::LOCAL_GET_EXNREF;125llvm_unreachable("Unexpected register class");126}127128/// Get the appropriate local.set opcode for the given register class.129static unsigned getLocalSetOpcode(const TargetRegisterClass *RC) {130if (RC == &WebAssembly::I32RegClass)131return WebAssembly::LOCAL_SET_I32;132if (RC == &WebAssembly::I64RegClass)133return WebAssembly::LOCAL_SET_I64;134if (RC == &WebAssembly::F32RegClass)135return WebAssembly::LOCAL_SET_F32;136if (RC == &WebAssembly::F64RegClass)137return WebAssembly::LOCAL_SET_F64;138if (RC == &WebAssembly::V128RegClass)139return WebAssembly::LOCAL_SET_V128;140if (RC == &WebAssembly::FUNCREFRegClass)141return WebAssembly::LOCAL_SET_FUNCREF;142if (RC == &WebAssembly::EXTERNREFRegClass)143return WebAssembly::LOCAL_SET_EXTERNREF;144if (RC == &WebAssembly::EXNREFRegClass)145return WebAssembly::LOCAL_SET_EXNREF;146llvm_unreachable("Unexpected register class");147}148149/// Get the appropriate local.tee opcode for the given register class.150static unsigned getLocalTeeOpcode(const TargetRegisterClass *RC) {151if (RC == &WebAssembly::I32RegClass)152return WebAssembly::LOCAL_TEE_I32;153if (RC == &WebAssembly::I64RegClass)154return WebAssembly::LOCAL_TEE_I64;155if (RC == &WebAssembly::F32RegClass)156return WebAssembly::LOCAL_TEE_F32;157if (RC == &WebAssembly::F64RegClass)158return WebAssembly::LOCAL_TEE_F64;159if (RC == &WebAssembly::V128RegClass)160return WebAssembly::LOCAL_TEE_V128;161if (RC == &WebAssembly::FUNCREFRegClass)162return WebAssembly::LOCAL_TEE_FUNCREF;163if (RC == &WebAssembly::EXTERNREFRegClass)164return WebAssembly::LOCAL_TEE_EXTERNREF;165if (RC == &WebAssembly::EXNREFRegClass)166return WebAssembly::LOCAL_TEE_EXNREF;167llvm_unreachable("Unexpected register class");168}169170/// Get the type associated with the given register class.171static MVT typeForRegClass(const TargetRegisterClass *RC) {172if (RC == &WebAssembly::I32RegClass)173return MVT::i32;174if (RC == &WebAssembly::I64RegClass)175return MVT::i64;176if (RC == &WebAssembly::F32RegClass)177return MVT::f32;178if (RC == &WebAssembly::F64RegClass)179return MVT::f64;180if (RC == &WebAssembly::V128RegClass)181return MVT::v16i8;182if (RC == &WebAssembly::FUNCREFRegClass)183return MVT::funcref;184if (RC == &WebAssembly::EXTERNREFRegClass)185return MVT::externref;186if (RC == &WebAssembly::EXNREFRegClass)187return MVT::exnref;188llvm_unreachable("unrecognized register class");189}190191/// Given a MachineOperand of a stackified vreg, return the instruction at the192/// start of the expression tree.193static MachineInstr *findStartOfTree(MachineOperand &MO,194MachineRegisterInfo &MRI,195const WebAssemblyFunctionInfo &MFI) {196Register Reg = MO.getReg();197assert(MFI.isVRegStackified(Reg));198MachineInstr *Def = MRI.getVRegDef(Reg);199200// If this instruction has any non-stackified defs, it is the start201for (auto DefReg : Def->defs()) {202if (!MFI.isVRegStackified(DefReg.getReg())) {203return Def;204}205}206207// Find the first stackified use and proceed from there.208for (MachineOperand &DefMO : Def->explicit_uses()) {209if (!DefMO.isReg())210continue;211return findStartOfTree(DefMO, MRI, MFI);212}213214// If there were no stackified uses, we've reached the start.215return Def;216}217218bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) {219LLVM_DEBUG(dbgs() << "********** Make Locals Explicit **********\n"220"********** Function: "221<< MF.getName() << '\n');222223bool Changed = false;224MachineRegisterInfo &MRI = MF.getRegInfo();225WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>();226const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();227228// Map non-stackified virtual registers to their local ids.229DenseMap<unsigned, unsigned> Reg2Local;230231// Handle ARGUMENTS first to ensure that they get the designated numbers.232for (MachineBasicBlock::iterator I = MF.begin()->begin(),233E = MF.begin()->end();234I != E;) {235MachineInstr &MI = *I++;236if (!WebAssembly::isArgument(MI.getOpcode()))237break;238Register Reg = MI.getOperand(0).getReg();239assert(!MFI.isVRegStackified(Reg));240auto Local = static_cast<unsigned>(MI.getOperand(1).getImm());241Reg2Local[Reg] = Local;242checkFrameBase(MFI, Local, Reg);243244// Update debug value to point to the local before removing.245WebAssemblyDebugValueManager(&MI).replaceWithLocal(Local);246247MI.eraseFromParent();248Changed = true;249}250251// Start assigning local numbers after the last parameter and after any252// already-assigned locals.253unsigned CurLocal = static_cast<unsigned>(MFI.getParams().size());254CurLocal += static_cast<unsigned>(MFI.getLocals().size());255256// Precompute the set of registers that are unused, so that we can insert257// drops to their defs.258BitVector UseEmpty(MRI.getNumVirtRegs());259for (unsigned I = 0, E = MRI.getNumVirtRegs(); I < E; ++I)260UseEmpty[I] = MRI.use_empty(Register::index2VirtReg(I));261262// Visit each instruction in the function.263for (MachineBasicBlock &MBB : MF) {264for (MachineInstr &MI : llvm::make_early_inc_range(MBB)) {265assert(!WebAssembly::isArgument(MI.getOpcode()));266267if (MI.isDebugInstr() || MI.isLabel())268continue;269270if (MI.getOpcode() == WebAssembly::IMPLICIT_DEF) {271MI.eraseFromParent();272Changed = true;273continue;274}275276// Replace tee instructions with local.tee. The difference is that tee277// instructions have two defs, while local.tee instructions have one def278// and an index of a local to write to.279//280// - Before:281// TeeReg, Reg = TEE DefReg282// INST ..., TeeReg, ...283// INST ..., Reg, ...284// INST ..., Reg, ...285// * DefReg: may or may not be stackified286// * Reg: not stackified287// * TeeReg: stackified288//289// - After (when DefReg was already stackified):290// TeeReg = LOCAL_TEE LocalId1, DefReg291// INST ..., TeeReg, ...292// INST ..., Reg, ...293// INST ..., Reg, ...294// * Reg: mapped to LocalId1295// * TeeReg: stackified296//297// - After (when DefReg was not already stackified):298// NewReg = LOCAL_GET LocalId1299// TeeReg = LOCAL_TEE LocalId2, NewReg300// INST ..., TeeReg, ...301// INST ..., Reg, ...302// INST ..., Reg, ...303// * DefReg: mapped to LocalId1304// * Reg: mapped to LocalId2305// * TeeReg: stackified306if (WebAssembly::isTee(MI.getOpcode())) {307assert(MFI.isVRegStackified(MI.getOperand(0).getReg()));308assert(!MFI.isVRegStackified(MI.getOperand(1).getReg()));309Register DefReg = MI.getOperand(2).getReg();310const TargetRegisterClass *RC = MRI.getRegClass(DefReg);311312// Stackify the input if it isn't stackified yet.313if (!MFI.isVRegStackified(DefReg)) {314unsigned LocalId = getLocalId(Reg2Local, MFI, CurLocal, DefReg);315Register NewReg = MRI.createVirtualRegister(RC);316unsigned Opc = getLocalGetOpcode(RC);317BuildMI(MBB, &MI, MI.getDebugLoc(), TII->get(Opc), NewReg)318.addImm(LocalId);319MI.getOperand(2).setReg(NewReg);320MFI.stackifyVReg(MRI, NewReg);321}322323// Replace the TEE with a LOCAL_TEE.324unsigned LocalId =325getLocalId(Reg2Local, MFI, CurLocal, MI.getOperand(1).getReg());326unsigned Opc = getLocalTeeOpcode(RC);327BuildMI(MBB, &MI, MI.getDebugLoc(), TII->get(Opc),328MI.getOperand(0).getReg())329.addImm(LocalId)330.addReg(MI.getOperand(2).getReg());331332WebAssemblyDebugValueManager(&MI).replaceWithLocal(LocalId);333334MI.eraseFromParent();335Changed = true;336continue;337}338339// Insert local.sets for any defs that aren't stackified yet.340for (auto &Def : MI.defs()) {341Register OldReg = Def.getReg();342if (!MFI.isVRegStackified(OldReg)) {343const TargetRegisterClass *RC = MRI.getRegClass(OldReg);344Register NewReg = MRI.createVirtualRegister(RC);345auto InsertPt = std::next(MI.getIterator());346if (UseEmpty[Register::virtReg2Index(OldReg)]) {347unsigned Opc = getDropOpcode(RC);348MachineInstr *Drop =349BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc))350.addReg(NewReg);351// After the drop instruction, this reg operand will not be used352Drop->getOperand(0).setIsKill();353if (MFI.isFrameBaseVirtual() && OldReg == MFI.getFrameBaseVreg())354MFI.clearFrameBaseVreg();355} else {356unsigned LocalId = getLocalId(Reg2Local, MFI, CurLocal, OldReg);357unsigned Opc = getLocalSetOpcode(RC);358359WebAssemblyDebugValueManager(&MI).replaceWithLocal(LocalId);360361BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc))362.addImm(LocalId)363.addReg(NewReg);364}365// This register operand of the original instruction is now being used366// by the inserted drop or local.set instruction, so make it not dead367// yet.368Def.setReg(NewReg);369Def.setIsDead(false);370MFI.stackifyVReg(MRI, NewReg);371Changed = true;372}373}374375// Insert local.gets for any uses that aren't stackified yet.376MachineInstr *InsertPt = &MI;377for (MachineOperand &MO : reverse(MI.explicit_uses())) {378if (!MO.isReg())379continue;380381Register OldReg = MO.getReg();382383// Inline asm may have a def in the middle of the operands. Our contract384// with inline asm register operands is to provide local indices as385// immediates.386if (MO.isDef()) {387assert(MI.isInlineAsm());388unsigned LocalId = getLocalId(Reg2Local, MFI, CurLocal, OldReg);389// If this register operand is tied to another operand, we can't390// change it to an immediate. Untie it first.391MI.untieRegOperand(MO.getOperandNo());392MO.ChangeToImmediate(LocalId);393continue;394}395396// If we see a stackified register, prepare to insert subsequent397// local.gets before the start of its tree.398if (MFI.isVRegStackified(OldReg)) {399InsertPt = findStartOfTree(MO, MRI, MFI);400continue;401}402403// Our contract with inline asm register operands is to provide local404// indices as immediates.405if (MI.isInlineAsm()) {406unsigned LocalId = getLocalId(Reg2Local, MFI, CurLocal, OldReg);407// Untie it first if this reg operand is tied to another operand.408MI.untieRegOperand(MO.getOperandNo());409MO.ChangeToImmediate(LocalId);410continue;411}412413// Insert a local.get.414unsigned LocalId = getLocalId(Reg2Local, MFI, CurLocal, OldReg);415const TargetRegisterClass *RC = MRI.getRegClass(OldReg);416Register NewReg = MRI.createVirtualRegister(RC);417unsigned Opc = getLocalGetOpcode(RC);418// Use a InsertPt as our DebugLoc, since MI may be discontinuous from419// the where this local is being inserted, causing non-linear stepping420// in the debugger or function entry points where variables aren't live421// yet. Alternative is previous instruction, but that is strictly worse422// since it can point at the previous statement.423// See crbug.com/1251909, crbug.com/1249745424InsertPt = BuildMI(MBB, InsertPt, InsertPt->getDebugLoc(),425TII->get(Opc), NewReg).addImm(LocalId);426MO.setReg(NewReg);427MFI.stackifyVReg(MRI, NewReg);428Changed = true;429}430431// Coalesce and eliminate COPY instructions.432if (WebAssembly::isCopy(MI.getOpcode())) {433MRI.replaceRegWith(MI.getOperand(1).getReg(),434MI.getOperand(0).getReg());435MI.eraseFromParent();436Changed = true;437}438}439}440441// Define the locals.442// TODO: Sort the locals for better compression.443MFI.setNumLocals(CurLocal - MFI.getParams().size());444for (unsigned I = 0, E = MRI.getNumVirtRegs(); I < E; ++I) {445Register Reg = Register::index2VirtReg(I);446auto RL = Reg2Local.find(Reg);447if (RL == Reg2Local.end() || RL->second < MFI.getParams().size())448continue;449450MFI.setLocal(RL->second - MFI.getParams().size(),451typeForRegClass(MRI.getRegClass(Reg)));452Changed = true;453}454455#ifndef NDEBUG456// Assert that all registers have been stackified at this point.457for (const MachineBasicBlock &MBB : MF) {458for (const MachineInstr &MI : MBB) {459if (MI.isDebugInstr() || MI.isLabel())460continue;461for (const MachineOperand &MO : MI.explicit_operands()) {462assert(463(!MO.isReg() || MRI.use_empty(MO.getReg()) ||464MFI.isVRegStackified(MO.getReg())) &&465"WebAssemblyExplicitLocals failed to stackify a register operand");466}467}468}469#endif470471return Changed;472}473474475