diff --git a/llvm/include/llvm/CodeGen/TargetInstrInfo.h b/llvm/include/llvm/CodeGen/TargetInstrInfo.h --- a/llvm/include/llvm/CodeGen/TargetInstrInfo.h +++ b/llvm/include/llvm/CodeGen/TargetInstrInfo.h @@ -2035,6 +2035,13 @@ return MI.getOperand(0); } + /// Returns true if the given \p MI defines a target index operand. If so, + /// sets \p Index and \p Offset of the target index operand. + virtual bool isTargetIndexDef(const MachineInstr &MI, int &Index, + int64_t &Offset) const { + return false; + } + private: mutable std::unique_ptr Formatter; unsigned CallFrameSetupOpcode, CallFrameDestroyOpcode; diff --git a/llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp b/llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp --- a/llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp +++ b/llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp @@ -10,12 +10,13 @@ /// /// LiveDebugValues is an optimistic "available expressions" dataflow /// algorithm. The set of expressions is the set of machine locations -/// (registers, spill slots, constants) that a variable fragment might be -/// located, qualified by a DIExpression and indirect-ness flag, while each -/// variable is identified by a DebugVariable object. The availability of an -/// expression begins when a DBG_VALUE instruction specifies the location of a -/// DebugVariable, and continues until that location is clobbered or -/// re-specified by a different DBG_VALUE for the same DebugVariable. +/// (registers, spill slots, constants, and target indices) that a variable +/// fragment might be located, qualified by a DIExpression and indirect-ness +/// flag, while each variable is identified by a DebugVariable object. The +/// availability of an expression begins when a DBG_VALUE instruction specifies +/// the location of a DebugVariable, and continues until that location is +/// clobbered or re-specified by a different DBG_VALUE for the same +/// DebugVariable. /// /// The output of LiveDebugValues is additional DBG_VALUE instructions, /// placed to extend variable locations as far they're available. This file @@ -230,6 +231,10 @@ static constexpr u32_location_t kEntryValueBackupLocation = kFirstInvalidRegLocation + 1; + /// A special location reserved for VarLocs with locations of kind + /// WasmLocKind. + static constexpr u32_location_t kWasmLocation = kFirstInvalidRegLocation + 2; + LocIndex(u32_location_t Location, u32_index_t Index) : Location(Location), Index(Index) {} @@ -675,6 +680,21 @@ llvm_unreachable("Could not find given SpillLoc in Locs"); } + bool containsWasmLocs() const { + return any_of(Locs, [](VarLoc::MachineLoc ML) { + return ML.Kind == VarLoc::MachineLocKind::WasmLocKind; + }); + } + + /// If this variable is described in whole or part by \p WasmLocation, + /// return true. + bool usesWasmLoc(WasmLoc WasmLocation) const { + MachineLoc WasmML; + WasmML.Kind = MachineLocKind::WasmLocKind; + WasmML.Value.WasmLocation = WasmLocation; + return is_contained(Locs, WasmML); + } + /// Determine whether the lexical scope of this value's debug location /// dominates MBB. bool dominates(LexicalScopes &LS, MachineBasicBlock &MBB) const { @@ -786,10 +806,10 @@ return RegNo < LocIndex::kFirstInvalidRegLocation; }) && "Physreg out of range?"); - if (VL.containsSpillLocs()) { - LocIndex::u32_location_t Loc = LocIndex::kSpillLocation; - Locations.push_back(Loc); - } + if (VL.containsSpillLocs()) + Locations.push_back(LocIndex::kSpillLocation); + if (VL.containsWasmLocs()) + Locations.push_back(LocIndex::kWasmLocation); } else if (VL.EVKind != VarLoc::EntryValueLocKind::EntryValueKind) { LocIndex::u32_location_t Loc = LocIndex::kEntryValueBackupLocation; Locations.push_back(Loc); @@ -942,6 +962,12 @@ return LocIndex::indexRangeForLocation( getVarLocs(), LocIndex::kEntryValueBackupLocation); } + + /// Get all set IDs for VarLocs with MLs of kind WasmLocKind. + auto getWasmVarLocs() const { + return LocIndex::indexRangeForLocation(getVarLocs(), + LocIndex::kWasmLocation); + } }; /// Collect all VarLoc IDs from \p CollectFrom for VarLocs with MLs of kind @@ -1028,6 +1054,8 @@ VarLocMap &VarLocIDs, InstToEntryLocMap &EntryValTransfers, RegDefToInstMap &RegSetInstrs); + void transferWasmDef(MachineInstr &MI, OpenRangesSet &OpenRanges, + VarLocMap &VarLocIDs); bool transferTerminator(MachineBasicBlock *MBB, OpenRangesSet &OpenRanges, VarLocInMBB &OutLocs, const VarLocMap &VarLocIDs); @@ -1608,6 +1636,30 @@ } } +void VarLocBasedLDV::transferWasmDef(MachineInstr &MI, + OpenRangesSet &OpenRanges, + VarLocMap &VarLocIDs) { + // If this is not a Wasm local.set or local.tee, which sets local values, + // return. + int Index; + int64_t Offset; + if (!TII->isTargetIndexDef(MI, Index, Offset)) + return; + + // Find the target indices killed by MI, and delete those variable locations + // fromthe open range. + VarLocsInRange KillSet; + VarLoc::WasmLoc Loc{Index, Offset}; + for (uint64_t ID : OpenRanges.getWasmVarLocs()) { + LocIndex Idx = LocIndex::fromRawInteger(ID); + const VarLoc &VL = VarLocIDs[Idx]; + assert(VL.containsWasmLocs() && "Broken VarLocSet?"); + if (VL.usesWasmLoc(Loc)) + KillSet.insert(ID); + } + OpenRanges.erase(KillSet, VarLocIDs, LocIndex::kWasmLocation); +} + bool VarLocBasedLDV::isSpillInstruction(const MachineInstr &MI, MachineFunction *MF) { // TODO: Handle multiple stores folded into one. @@ -1946,6 +1998,7 @@ RegSetInstrs); transferRegisterDef(MI, OpenRanges, VarLocIDs, EntryValTransfers, RegSetInstrs); + transferWasmDef(MI, OpenRanges, VarLocIDs); transferRegisterCopy(MI, OpenRanges, VarLocIDs, Transfers); transferSpillOrRestoreInst(MI, OpenRanges, VarLocIDs, Transfers); } @@ -2259,6 +2312,11 @@ // Ranges have converged when both worklists are empty. SmallPtrSet Visited; while (!Worklist.empty() || !Pending.empty()) { + if (MF.getName() == "test_target_index_defs") { + errs() << "break\n"; + errs() << "break\n"; + errs() << "break\n"; + } // We track what is on the pending worklist to avoid inserting the same // thing twice. We could avoid this with a custom priority queue, but this // is probably not worth it. diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h @@ -416,6 +416,72 @@ } } +inline bool isLocalSet(unsigned Opc) { + switch (Opc) { + case WebAssembly::LOCAL_SET_I32: + case WebAssembly::LOCAL_SET_I32_S: + case WebAssembly::LOCAL_SET_I64: + case WebAssembly::LOCAL_SET_I64_S: + case WebAssembly::LOCAL_SET_F32: + case WebAssembly::LOCAL_SET_F32_S: + case WebAssembly::LOCAL_SET_F64: + case WebAssembly::LOCAL_SET_F64_S: + case WebAssembly::LOCAL_SET_V128: + case WebAssembly::LOCAL_SET_V128_S: + case WebAssembly::LOCAL_SET_FUNCREF: + case WebAssembly::LOCAL_SET_FUNCREF_S: + case WebAssembly::LOCAL_SET_EXTERNREF: + case WebAssembly::LOCAL_SET_EXTERNREF_S: + return true; + default: + return false; + } +} + +inline bool isLocalTee(unsigned Opc) { + switch (Opc) { + case WebAssembly::LOCAL_TEE_I32: + case WebAssembly::LOCAL_TEE_I32_S: + case WebAssembly::LOCAL_TEE_I64: + case WebAssembly::LOCAL_TEE_I64_S: + case WebAssembly::LOCAL_TEE_F32: + case WebAssembly::LOCAL_TEE_F32_S: + case WebAssembly::LOCAL_TEE_F64: + case WebAssembly::LOCAL_TEE_F64_S: + case WebAssembly::LOCAL_TEE_V128: + case WebAssembly::LOCAL_TEE_V128_S: + case WebAssembly::LOCAL_TEE_FUNCREF: + case WebAssembly::LOCAL_TEE_FUNCREF_S: + case WebAssembly::LOCAL_TEE_EXTERNREF: + case WebAssembly::LOCAL_TEE_EXTERNREF_S: + return true; + default: + return false; + } +} + +inline bool isLocalGet(unsigned Opc) { + switch (Opc) { + case WebAssembly::LOCAL_GET_I32: + case WebAssembly::LOCAL_GET_I32_S: + case WebAssembly::LOCAL_GET_I64: + case WebAssembly::LOCAL_GET_I64_S: + case WebAssembly::LOCAL_GET_F32: + case WebAssembly::LOCAL_GET_F32_S: + case WebAssembly::LOCAL_GET_F64: + case WebAssembly::LOCAL_GET_F64_S: + case WebAssembly::LOCAL_GET_V128: + case WebAssembly::LOCAL_GET_V128_S: + case WebAssembly::LOCAL_GET_FUNCREF: + case WebAssembly::LOCAL_GET_FUNCREF_S: + case WebAssembly::LOCAL_GET_EXTERNREF: + case WebAssembly::LOCAL_GET_EXTERNREF_S: + return true; + default: + return false; + } +} + } // end namespace WebAssembly } // end namespace llvm diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.h b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.h --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.h @@ -69,6 +69,9 @@ getSerializableTargetIndices() const override; const MachineOperand &getCalleeOperand(const MachineInstr &MI) const override; + + bool isTargetIndexDef(const MachineInstr &MI, int &Index, + int64_t &Offset) const override; }; } // end namespace llvm diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp @@ -204,3 +204,14 @@ WebAssemblyInstrInfo::getCalleeOperand(const MachineInstr &MI) const { return WebAssembly::getCalleeOp(MI); } + +bool WebAssemblyInstrInfo::isTargetIndexDef(const MachineInstr &MI, int &Index, + int64_t &Offset) const { + if (WebAssembly::isLocalSet(MI.getOpcode()) || + WebAssembly::isLocalTee(MI.getOpcode())) { + Index = WebAssembly::TI_LOCAL; + Offset = MI.explicit_uses().begin()->getImm(); + return true; + } + return false; +} diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -509,7 +509,6 @@ disablePass(&PostRASchedulerID); disablePass(&FuncletLayoutID); disablePass(&StackMapLivenessID); - disablePass(&LiveDebugValuesID); disablePass(&PatchableFunctionID); disablePass(&ShrinkWrapID); diff --git a/llvm/test/DebugInfo/WebAssembly/live-debug-values.mir b/llvm/test/DebugInfo/WebAssembly/live-debug-values.mir --- a/llvm/test/DebugInfo/WebAssembly/live-debug-values.mir +++ b/llvm/test/DebugInfo/WebAssembly/live-debug-values.mir @@ -22,6 +22,13 @@ ret void } + define void @test_target_index_defs() !dbg !21 { + call void @llvm.dbg.value(metadata i32 0, metadata !22, metadata !DIExpression()), !dbg !23 + call void @llvm.dbg.value(metadata i32 0, metadata !24, metadata !DIExpression()), !dbg !23 + call void @llvm.dbg.value(metadata i32 0, metadata !25, metadata !DIExpression()), !dbg !23 + ret void + } + declare void @llvm.dbg.value(metadata, metadata, metadata) !llvm.dbg.cu = !{!0} @@ -48,6 +55,11 @@ !18 = !DILocalVariable(name: "var2", scope: !14, file: !1, line: 11, type: !8) !19 = !DILocalVariable(name: "var3", scope: !14, file: !1, line: 11, type: !8) !20 = !DILocalVariable(name: "var4", scope: !14, file: !1, line: 11, type: !8) + !21 = distinct !DISubprogram(name: "test_target_index_defs", scope: !1, file: !1, line: 10, type: !6, scopeLine: 1, unit: !0) + !22 = !DILocalVariable(name: "var0", scope: !21, file: !1, line: 20, type: !8) + !23 = !DILocation(line: 10, scope: !21) + !24 = !DILocalVariable(name: "var1", scope: !21, file: !1, line: 20, type: !8) + !25 = !DILocalVariable(name: "var2", scope: !21, file: !1, line: 20, type: !8) ... --- @@ -168,3 +180,43 @@ ; predecessors: %bb.1, %bb.2 RETURN implicit-def dead $arguments ... + +--- +# bb.0 defines DBG_VALUEs for local index 2 and 3. The DBG_VALUE for local index +# 2 is killed in bb.1 due to 'local.tee 2' there, and DBG_VALUE for local +# index 3 is killed in bb.2 because there is 'local.set 3' there. As a result, +# bb.3 shoudln't have any additional DBG_VALUEs added for those locals. +# CHECK-LABEL: name: test_target_index_defs +name: test_target_index_defs +liveins: + - { reg: '$arguments' } +body: | + bb.0: + successors: %bb.1, %bb.2 + liveins: $arguments + DBG_VALUE target-index(wasm-local) + 2, $noreg, !22, !DIExpression(), debug-location !23 + DBG_VALUE target-index(wasm-local) + 3, $noreg, !24, !DIExpression(), debug-location !23 + %0:i32 = CONST_I32 1, implicit-def $arguments + BR_IF %bb.2, %0:i32, implicit-def $arguments + + bb.1: + ; predecessors: %bb.0 + successors: %bb.3 + %0:i32 = CONST_I32 1, implicit-def $arguments + %1:i32 = LOCAL_TEE_I32 2, %0:i32, implicit-def $arguments + DROP_I32 killed %1:i32, implicit-def $arguments + BR %bb.3, implicit-def $arguments + + bb.2: + ; predecessors: %bb.0 + successors: %bb.3 + %0:i32 = CONST_I32 1, implicit-def $arguments + LOCAL_SET_I32 3, %0:i32, implicit-def $arguments + BR %bb.3, implicit-def $arguments + + ; CHECK: bb.3: + ; CHECK-NOT: DBG_VALUE target-index(wasm-local){{.*}} + bb.3: + ; predecessors: %bb.1, %bb.2 + RETURN implicit-def dead $arguments +...