| //===-- WebAssemblyDebugValueManager.cpp - WebAssembly DebugValue Manager -===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| /// |
| /// \file |
| /// This file implements the manager for MachineInstr DebugValues. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #include "WebAssemblyDebugValueManager.h" |
| #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" |
| #include "WebAssembly.h" |
| #include "WebAssemblyMachineFunctionInfo.h" |
| #include "llvm/CodeGen/MachineInstr.h" |
| #include "llvm/IR/DebugInfoMetadata.h" |
| |
| using namespace llvm; |
| |
| WebAssemblyDebugValueManager::WebAssemblyDebugValueManager(MachineInstr *Def) |
| : Def(Def) { |
| // This code differs from MachineInstr::collectDebugValues in that it scans |
| // the whole BB, not just contiguous DBG_VALUEs, until another definition to |
| // the same register is encountered. |
| if (!Def->getOperand(0).isReg()) |
| return; |
| CurrentReg = Def->getOperand(0).getReg(); |
| |
| for (MachineBasicBlock::iterator MI = std::next(Def->getIterator()), |
| ME = Def->getParent()->end(); |
| MI != ME; ++MI) { |
| // If another definition appears, stop |
| if (MI->definesRegister(CurrentReg)) |
| break; |
| if (MI->isDebugValue() && MI->hasDebugOperandForReg(CurrentReg)) |
| DbgValues.push_back(&*MI); |
| } |
| } |
| |
| // Returns true if both A and B are the same CONST_I32/I64/F32/F64 instructions. |
| // Doesn't include CONST_V128. |
| static bool isSameScalarConst(const MachineInstr *A, const MachineInstr *B) { |
| if (A->getOpcode() != B->getOpcode() || |
| !WebAssembly::isScalarConst(A->getOpcode()) || |
| !WebAssembly::isScalarConst(B->getOpcode())) |
| return false; |
| const MachineOperand &OpA = A->getOperand(1), &OpB = B->getOperand(1); |
| if ((OpA.isImm() && OpB.isImm() && OpA.getImm() == OpB.getImm()) || |
| (OpA.isFPImm() && OpB.isFPImm() && OpA.getFPImm() == OpB.getFPImm()) || |
| (OpA.isGlobal() && OpB.isGlobal() && OpA.getGlobal() == OpB.getGlobal())) |
| return true; |
| return false; |
| } |
| |
| SmallVector<MachineInstr *, 1> |
| WebAssemblyDebugValueManager::getSinkableDebugValues( |
| MachineInstr *Insert) const { |
| if (DbgValues.empty()) |
| return {}; |
| // DBG_VALUEs between Def and Insert |
| SmallVector<MachineInstr *, 8> DbgValuesInBetween; |
| |
| if (Def->getParent() == Insert->getParent()) { |
| // When Def and Insert are within the same BB, check if Insert comes after |
| // Def, because we only support sinking. |
| bool DefFirst = false; |
| for (MachineBasicBlock::iterator MI = std::next(Def->getIterator()), |
| ME = Def->getParent()->end(); |
| MI != ME; ++MI) { |
| if (&*MI == Insert) { |
| DefFirst = true; |
| break; |
| } |
| if (MI->isDebugValue()) |
| DbgValuesInBetween.push_back(&*MI); |
| } |
| if (!DefFirst) // Not a sink |
| return {}; |
| |
| } else { // Def and Insert are in different BBs |
| // If Def and Insert are in different BBs, we only handle a simple case in |
| // which Insert's BB is a successor of Def's BB. |
| if (!Def->getParent()->isSuccessor(Insert->getParent())) |
| return {}; |
| |
| // Gather DBG_VALUEs between 'Def~Def BB's end' and |
| // 'Insert BB's begin~Insert' |
| for (MachineBasicBlock::iterator MI = std::next(Def->getIterator()), |
| ME = Def->getParent()->end(); |
| MI != ME; ++MI) { |
| if (MI->isDebugValue()) |
| DbgValuesInBetween.push_back(&*MI); |
| } |
| for (MachineBasicBlock::iterator MI = Insert->getParent()->begin(), |
| ME = Insert->getIterator(); |
| MI != ME; ++MI) { |
| if (MI->isDebugValue()) |
| DbgValuesInBetween.push_back(&*MI); |
| } |
| } |
| |
| // Gather DebugVariables that are seen between Def and Insert, excluding our |
| // own DBG_VALUEs in DbgValues. |
| SmallDenseMap<DebugVariable, SmallVector<MachineInstr *, 2>> |
| SeenDbgVarToDbgValues; |
| for (auto *DV : DbgValuesInBetween) { |
| if (!llvm::is_contained(DbgValues, DV)) { |
| DebugVariable Var(DV->getDebugVariable(), DV->getDebugExpression(), |
| DV->getDebugLoc()->getInlinedAt()); |
| SeenDbgVarToDbgValues[Var].push_back(DV); |
| } |
| } |
| |
| // Gather sinkable DBG_VALUEs. We should not sink a DBG_VALUE if there is |
| // another DBG_VALUE between Def and Insert referring to the same |
| // DebugVariable. For example, |
| // %0 = someinst |
| // DBG_VALUE %0, !"a", !DIExpression() // Should not sink with %0 |
| // %1 = anotherinst |
| // DBG_VALUE %1, !"a", !DIExpression() |
| // Where if %0 were to sink, the DBG_VAUE should not sink with it, as that |
| // would re-order assignments. |
| SmallVector<MachineInstr *, 1> SinkableDbgValues; |
| MachineRegisterInfo &MRI = Def->getParent()->getParent()->getRegInfo(); |
| for (auto *DV : DbgValues) { |
| DebugVariable Var(DV->getDebugVariable(), DV->getDebugExpression(), |
| DV->getDebugLoc()->getInlinedAt()); |
| auto It = SeenDbgVarToDbgValues.find(Var); |
| if (It == SeenDbgVarToDbgValues.end()) { |
| SinkableDbgValues.push_back(DV); |
| continue; |
| } |
| if (!WebAssembly::isScalarConst(Def->getOpcode())) |
| continue; |
| auto &OverlappingDbgValues = It->second; |
| bool Sinkable = true; |
| for (auto *OverlappingDV : OverlappingDbgValues) { |
| MachineOperand &DbgOp = OverlappingDV->getDebugOperand(0); |
| if (!DbgOp.isReg()) { |
| Sinkable = false; |
| break; |
| } |
| Register OtherReg = DbgOp.getReg(); |
| MachineInstr *OtherDef = MRI.getUniqueVRegDef(OtherReg); |
| // We have an exception to allow encoutering other DBG_VALUEs with the |
| // smae DebugVariables, only when they are referring to the same scalar |
| // CONST instruction. For example, |
| // %0 = CONST_I32 1 |
| // DBG_VALUE %0, !"a", !DIExpression() // Can sink with %0 |
| // %1 = CONST_I32 1 |
| // DBG_VALUE %1, !"a", !DIExpression() |
| // When %0 were to be sunk/cloneed, the DBG_VALUE can be sunk/cloned with |
| // it because even though the second DBG_VALUE refers to the same |
| // DebugVariable, its value in effect is the same CONST instruction. |
| // |
| // This is to allow a case that can happen with RegStackify's |
| // "rematerializeCheapDef". For example, we have this program with two |
| // BBs: |
| // bb0: |
| // %0 = CONST_I32 1 |
| // DBG_VALUE %0, !"a", ... |
| // ... |
| // INST0 ..., $0 ... |
| // bb1: |
| // INST1 ..., $0 ... |
| // INST2 ..., $0 ... |
| // |
| // We process bb0 first. Because %0 is used multiple times, %0 is cloned |
| // before INST0: |
| // bb0: |
| // %0 = CONST_I32 1 |
| // DBG_VALUE %0, !"a", ... |
| // ... |
| // %1 = CONST_I32 1 |
| // DBG_VALUE %1, !"a", ... |
| // INST0 ..., $1 ... |
| // |
| // And when we process bb1, we clone %0 and its DBG_VALUE again: |
| // bb0: |
| // %0 = CONST_I32 1 |
| // DBG_VALUE %0, !"a", ... |
| // ... |
| // %1 = CONST_I32 1 |
| // DBG_VALUE %1, !"a", ... |
| // INST0 ..., $1 ... |
| // bb1: |
| // %2 = CONST_I32 1 |
| // DBG_VALUE %2, !"a", ... // !!! |
| // INST1 ..., $2 ... |
| // %3 = CONST_I32 1 |
| // DBG_VALUE %3, !"a", ... // !!! |
| // INST2 ..., $3 ... |
| // |
| // But (without this exception) the cloned DBG_VALUEs marked with !!! are |
| // not possible to be cloned, because there is a previously cloned |
| // 'DBG_VALUE %1, !"a"' at the end of bb0 referring to the same |
| // DebugVariable "a". But in this case they are OK to be cloned, because |
| // the interfering DBG_VALUE is pointing to the same 'CONST_I32 1', |
| // because it was cloned from the same instruction. |
| if (!OtherDef || !isSameScalarConst(Def, OtherDef)) { |
| Sinkable = false; |
| break; |
| } |
| } |
| if (Sinkable) |
| SinkableDbgValues.push_back(DV); |
| } |
| return SinkableDbgValues; |
| } |
| |
| // Returns true if the insertion point is the same as the current place. |
| // Following DBG_VALUEs for 'Def' are ignored. |
| bool WebAssemblyDebugValueManager::isInsertSamePlace( |
| MachineInstr *Insert) const { |
| if (Def->getParent() != Insert->getParent()) |
| return false; |
| for (MachineBasicBlock::iterator MI = std::next(Def->getIterator()), |
| ME = Insert; |
| MI != ME; ++MI) { |
| if (!llvm::is_contained(DbgValues, MI)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| // Returns true if any instruction in MBB has the same debug location as DL. |
| // Also returns true if DL is an empty location. |
| static bool hasSameDebugLoc(const MachineBasicBlock *MBB, DebugLoc DL) { |
| for (const auto &MI : *MBB) |
| if (MI.getDebugLoc() == DL) |
| return true; |
| return false; |
| } |
| |
| // Sink 'Def', and also sink its eligible DBG_VALUEs to the place before |
| // 'Insert'. Convert the original DBG_VALUEs into undefs. |
| // |
| // For DBG_VALUEs to sink properly, if 'Def' and 'Insert' are within the same |
| // BB, 'Insert' should be below 'Def'; if they are in different BBs, 'Insert' |
| // should be in one of 'Def's BBs successors. Def will be sunk regardless of the |
| // location. |
| // |
| // This DebugValueManager's new Def and DbgValues will be updated to the newly |
| // sinked Def + DBG_VALUEs. |
| void WebAssemblyDebugValueManager::sink(MachineInstr *Insert) { |
| // In case Def is requested to be sunk to |
| // the same place, we don't need to do anything. If we actually do the sink, |
| // it will create unnecessary undef DBG_VALUEs. For example, if the original |
| // code is: |
| // %0 = someinst // Def |
| // DBG_VALUE %0, ... |
| // %1 = anotherinst // Insert |
| // |
| // If we actually sink %0 and the following DBG_VALUE and setting the original |
| // DBG_VALUE undef, the result will be: |
| // DBG_VALUE %noreg, ... // Unnecessary! |
| // %0 = someinst // Def |
| // DBG_VALUE %0, ... |
| // %1 = anotherinst // Insert |
| if (isInsertSamePlace(Insert)) |
| return; |
| |
| MachineBasicBlock *MBB = Insert->getParent(); |
| MachineFunction *MF = MBB->getParent(); |
| |
| // Get the list of sinkable DBG_VALUEs. This should be done before sinking |
| // Def, because we need to examine instructions between Def and Insert. |
| SmallVector<MachineInstr *, 1> SinkableDbgValues = |
| getSinkableDebugValues(Insert); |
| |
| // Sink Def first. |
| // |
| // When moving to a different BB, we preserve the debug loc only if the |
| // destination BB contains the same location. See |
| // https://llvm.org/docs/HowToUpdateDebugInfo.html#when-to-preserve-an-instruction-location. |
| if (Def->getParent() != MBB && !hasSameDebugLoc(MBB, Def->getDebugLoc())) |
| Def->setDebugLoc(DebugLoc()); |
| MBB->splice(Insert, Def->getParent(), Def); |
| |
| if (DbgValues.empty()) |
| return; |
| |
| // Clone sinkable DBG_VALUEs and insert them. |
| SmallVector<MachineInstr *, 1> NewDbgValues; |
| for (MachineInstr *DV : SinkableDbgValues) { |
| MachineInstr *Clone = MF->CloneMachineInstr(DV); |
| MBB->insert(Insert, Clone); |
| NewDbgValues.push_back(Clone); |
| } |
| |
| // When sinking a Def and its DBG_VALUEs, we shouldn't just remove the |
| // original DBG_VALUE instructions; we should set them to undef not to create |
| // an impossible combination of variable assignments in the original program. |
| // For example, this is the original program in order: |
| // %0 = CONST_I32 0 |
| // DBG_VALUE %0, !"a", !DIExpression() // a = 0, b = ? |
| // %1 = CONST_I32 1 |
| // DBG_VALUE %1, !"b", !DIExpression() // a = 0, b = 1 |
| // %2 = CONST_I32 2 |
| // DBG_VALUE %2, !"a", !DIExpression() // a = 2, b = 1 |
| // %3 = CONST_I32 3 |
| // DBG_VALUE %3, !"b", !DIExpression() // a = 2, b = 3 |
| // |
| // If %2 were to sink below %3, if we just sink DBG_VALUE %1 with it, the |
| // debug info will show the variable "b" is updated to 2, creating the |
| // variable assignment combination of (a = 0, b = 3), which is not possible in |
| // the original program: |
| // %0 = CONST_I32 0 |
| // DBG_VALUE %0, !"a", !DIExpression() // a = 0, b = ? |
| // %1 = CONST_I32 1 |
| // DBG_VALUE %1, !"b", !DIExpression() // a = 0, b = 1 |
| // %3 = CONST_I32 3 |
| // DBG_VALUE %3, !"b", !DIExpression() // a = 0, b = 3 (Incorrect!) |
| // %2 = CONST_I32 2 |
| // DBG_VALUE %2, !"a", !DIExpression() // a = 2, b = 3 |
| // |
| // To fix this,we leave an undef DBG_VALUE in its original place, so that the |
| // result will be |
| // %0 = CONST_I32 0 |
| // DBG_VALUE %0, !"a", !DIExpression() // a = 0, b = ? |
| // %1 = CONST_I32 1 |
| // DBG_VALUE %1, !"b", !DIExpression() // a = 0, b = 1 |
| // DBG_VALUE $noreg, !"a", !DIExpression() // a = ?, b = 1 |
| // %3 = CONST_I32 3 |
| // DBG_VALUE %3, !"b", !DIExpression() // a = ?, b = 3 |
| // %2 = CONST_I32 2 |
| // DBG_VALUE %2, !"a", !DIExpression() // a = 2, b = 3 |
| // Now in the middle "a" will be shown as "optimized out", but it wouldn't |
| // show the impossible combination of (a = 0, b = 3). |
| for (MachineInstr *DV : DbgValues) |
| DV->setDebugValueUndef(); |
| |
| DbgValues.swap(NewDbgValues); |
| } |
| |
| // Clone 'Def', and also clone its eligible DBG_VALUEs to the place before |
| // 'Insert'. |
| // |
| // For DBG_VALUEs to be cloned properly, if 'Def' and 'Insert' are within the |
| // same BB, 'Insert' should be below 'Def'; if they are in different BBs, |
| // 'Insert' should be in one of 'Def's BBs successors. Def will be cloned |
| // regardless of the location. |
| // |
| // If NewReg is not $noreg, the newly cloned DBG_VALUEs will have the new |
| // register as its operand. |
| void WebAssemblyDebugValueManager::cloneSink(MachineInstr *Insert, |
| Register NewReg, |
| bool CloneDef) const { |
| MachineBasicBlock *MBB = Insert->getParent(); |
| MachineFunction *MF = MBB->getParent(); |
| |
| SmallVector<MachineInstr *> SinkableDbgValues = |
| getSinkableDebugValues(Insert); |
| |
| // Clone Def first. |
| if (CloneDef) { |
| MachineInstr *Clone = MF->CloneMachineInstr(Def); |
| // When cloning to a different BB, we preserve the debug loc only if the |
| // destination BB contains the same location. See |
| // https://llvm.org/docs/HowToUpdateDebugInfo.html#when-to-preserve-an-instruction-location. |
| if (Def->getParent() != MBB && !hasSameDebugLoc(MBB, Def->getDebugLoc())) |
| Clone->setDebugLoc(DebugLoc()); |
| if (NewReg != CurrentReg && NewReg.isValid()) |
| Clone->getOperand(0).setReg(NewReg); |
| MBB->insert(Insert, Clone); |
| } |
| |
| if (DbgValues.empty()) |
| return; |
| |
| // Clone sinkable DBG_VALUEs and insert them. |
| SmallVector<MachineInstr *, 1> NewDbgValues; |
| for (MachineInstr *DV : SinkableDbgValues) { |
| MachineInstr *Clone = MF->CloneMachineInstr(DV); |
| MBB->insert(Insert, Clone); |
| NewDbgValues.push_back(Clone); |
| } |
| |
| if (NewReg != CurrentReg && NewReg.isValid()) |
| for (auto *DBI : NewDbgValues) |
| for (auto &MO : DBI->getDebugOperandsForReg(CurrentReg)) |
| MO.setReg(NewReg); |
| } |
| |
| // Update the register for Def and DBG_VALUEs. |
| void WebAssemblyDebugValueManager::updateReg(Register Reg) { |
| if (Reg != CurrentReg && Reg.isValid()) { |
| for (auto *DBI : DbgValues) |
| for (auto &MO : DBI->getDebugOperandsForReg(CurrentReg)) |
| MO.setReg(Reg); |
| CurrentReg = Reg; |
| Def->getOperand(0).setReg(Reg); |
| } |
| } |
| |
| void WebAssemblyDebugValueManager::replaceWithLocal(unsigned LocalId) { |
| for (auto *DBI : DbgValues) { |
| auto IndexType = DBI->isIndirectDebugValue() |
| ? llvm::WebAssembly::TI_LOCAL_INDIRECT |
| : llvm::WebAssembly::TI_LOCAL; |
| for (auto &MO : DBI->getDebugOperandsForReg(CurrentReg)) |
| MO.ChangeToTargetIndex(IndexType, LocalId); |
| } |
| } |
| |
| // Remove Def, and set its DBG_VALUEs to undef. |
| void WebAssemblyDebugValueManager::removeDef() { |
| Def->removeFromParent(); |
| for (MachineInstr *DV : DbgValues) |
| DV->setDebugValueUndef(); |
| } |