2017-02-22 23:32:51 +01:00
|
|
|
//===- StackMaps.cpp ------------------------------------------------------===//
|
2013-10-31 23:11:56 +01:00
|
|
|
//
|
2019-01-19 09:50:56 +01:00
|
|
|
// 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
|
2013-10-31 23:11:56 +01:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2017-06-06 13:49:48 +02:00
|
|
|
#include "llvm/CodeGen/StackMaps.h"
|
2017-02-22 23:32:51 +01:00
|
|
|
#include "llvm/ADT/DenseMapInfo.h"
|
|
|
|
#include "llvm/ADT/STLExtras.h"
|
|
|
|
#include "llvm/ADT/Twine.h"
|
2013-10-31 23:11:56 +01:00
|
|
|
#include "llvm/CodeGen/AsmPrinter.h"
|
2014-01-30 19:58:27 +01:00
|
|
|
#include "llvm/CodeGen/MachineFrameInfo.h"
|
2014-03-04 11:07:28 +01:00
|
|
|
#include "llvm/CodeGen/MachineFunction.h"
|
2013-10-31 23:11:56 +01:00
|
|
|
#include "llvm/CodeGen/MachineInstr.h"
|
2017-02-22 23:32:51 +01:00
|
|
|
#include "llvm/CodeGen/MachineOperand.h"
|
2017-11-17 02:07:10 +01:00
|
|
|
#include "llvm/CodeGen/TargetOpcodes.h"
|
|
|
|
#include "llvm/CodeGen/TargetRegisterInfo.h"
|
|
|
|
#include "llvm/CodeGen/TargetSubtargetInfo.h"
|
2013-11-29 04:07:54 +01:00
|
|
|
#include "llvm/IR/DataLayout.h"
|
2013-10-31 23:11:56 +01:00
|
|
|
#include "llvm/MC/MCContext.h"
|
|
|
|
#include "llvm/MC/MCExpr.h"
|
2013-11-08 23:30:52 +01:00
|
|
|
#include "llvm/MC/MCObjectFileInfo.h"
|
2017-02-22 23:32:51 +01:00
|
|
|
#include "llvm/MC/MCRegisterInfo.h"
|
2013-10-31 23:11:56 +01:00
|
|
|
#include "llvm/MC/MCStreamer.h"
|
2014-05-02 00:21:30 +02:00
|
|
|
#include "llvm/Support/CommandLine.h"
|
2017-02-22 23:32:51 +01:00
|
|
|
#include "llvm/Support/Debug.h"
|
|
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
|
|
#include "llvm/Support/MathExtras.h"
|
|
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#include <cassert>
|
|
|
|
#include <cstdint>
|
2013-10-31 23:11:56 +01:00
|
|
|
#include <iterator>
|
2017-02-22 23:32:51 +01:00
|
|
|
#include <utility>
|
2013-10-31 23:11:56 +01:00
|
|
|
|
|
|
|
using namespace llvm;
|
|
|
|
|
2014-04-22 04:02:50 +02:00
|
|
|
#define DEBUG_TYPE "stackmaps"
|
|
|
|
|
2015-07-09 00:42:09 +02:00
|
|
|
static cl::opt<int> StackMapVersion(
|
2017-12-01 01:53:10 +01:00
|
|
|
"stackmap-version", cl::init(3), cl::Hidden,
|
2017-04-28 06:48:42 +02:00
|
|
|
cl::desc("Specify the stackmap encoding version (default = 3)"));
|
2014-05-02 00:21:30 +02:00
|
|
|
|
2014-05-02 00:39:26 +02:00
|
|
|
const char *StackMaps::WSMP = "Stack Maps: ";
|
|
|
|
|
[Statepoints] Change statepoint machine instr format to better suit VReg lowering.
Current Statepoint MI format is this:
STATEPOINT
<id>, <num patch bytes >, <num call arguments>, <call target>,
[call arguments...],
<StackMaps::ConstantOp>, <calling convention>,
<StackMaps::ConstantOp>, <statepoint flags>,
<StackMaps::ConstantOp>, <num deopt args>, [deopt args...],
<gc base/derived pairs...> <gc allocas...>
Note that GC pointers are listed in pairs <base,derived>.
This causes base pointers to appear many times (at least twice) in
instruction, which is bad for us when VReg lowering is ON.
The problem is that machine operand tiedness is 1-1 relation, so
it might look like this:
%vr2 = STATEPOINT ... %vr1, %vr1(tied-def0)
Since only one instance of %vr1 is tied, that may lead to incorrect
codegen (see PR46917 for more details), so we have to always spill
base pointers. This mostly defeats new VReg lowering scheme.
This patch changes statepoint instruction format so that every
gc pointer appears only once in operand list. That way they all can
be tied. Additional set of operands is added to preserve base-derived
relation required to build stackmap.
New statepoint has following format:
STATEPOINT
<id>, <num patch bytes>, <num call arguments>, <call target>,
[call arguments...],
<StackMaps::ConstantOp>, <calling convention>,
<StackMaps::ConstantOp>, <statepoint flags>,
<StackMaps::ConstantOp>, <num deopt args>, [deopt args...],
<StackMaps::ConstantOp>, <num gc pointers>, [gc pointers...],
<StackMaps::ConstantOp>, <num gc allocas>, [gc allocas...]
<StackMaps::ConstantOp>, <num entries in gc map>, [base/derived indices...]
Changes are:
- every gc pointer is listed only once in a flat length-prefixed list;
- alloca list is prefixed with its length too;
- following alloca list is length-prefixed list of base-derived
indices of pointers from gc pointer list. Note that indices are
logical (number of pointer), not absolute (index of machine operand).
Differential Revision: https://reviews.llvm.org/D87154
2020-09-04 19:45:41 +02:00
|
|
|
static uint64_t getConstMetaVal(const MachineInstr &MI, unsigned Idx) {
|
|
|
|
assert(MI.getOperand(Idx).isImm() &&
|
|
|
|
MI.getOperand(Idx).getImm() == StackMaps::ConstantOp);
|
|
|
|
const auto &MO = MI.getOperand(Idx + 1);
|
|
|
|
assert(MO.isImm());
|
|
|
|
return MO.getImm();
|
|
|
|
}
|
|
|
|
|
2016-08-24 01:33:29 +02:00
|
|
|
StackMapOpers::StackMapOpers(const MachineInstr *MI)
|
|
|
|
: MI(MI) {
|
2016-08-23 23:21:43 +02:00
|
|
|
assert(getVarIdx() <= MI->getNumOperands() &&
|
|
|
|
"invalid stackmap definition");
|
|
|
|
}
|
|
|
|
|
2013-12-15 00:06:19 +01:00
|
|
|
PatchPointOpers::PatchPointOpers(const MachineInstr *MI)
|
2015-07-09 00:42:09 +02:00
|
|
|
: MI(MI), HasDef(MI->getOperand(0).isReg() && MI->getOperand(0).isDef() &&
|
2016-08-24 01:58:08 +02:00
|
|
|
!MI->getOperand(0).isImplicit()) {
|
2013-11-19 04:29:56 +01:00
|
|
|
#ifndef NDEBUG
|
|
|
|
unsigned CheckStartIdx = 0, e = MI->getNumOperands();
|
|
|
|
while (CheckStartIdx < e && MI->getOperand(CheckStartIdx).isReg() &&
|
|
|
|
MI->getOperand(CheckStartIdx).isDef() &&
|
|
|
|
!MI->getOperand(CheckStartIdx).isImplicit())
|
|
|
|
++CheckStartIdx;
|
|
|
|
|
|
|
|
assert(getMetaIdx() == CheckStartIdx &&
|
2014-01-24 18:20:08 +01:00
|
|
|
"Unexpected additional definition in Patchpoint intrinsic.");
|
2013-11-19 04:29:56 +01:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned PatchPointOpers::getNextScratchIdx(unsigned StartIdx) const {
|
|
|
|
if (!StartIdx)
|
|
|
|
StartIdx = getVarIdx();
|
|
|
|
|
|
|
|
// Find the next scratch register (implicit def and early clobber)
|
|
|
|
unsigned ScratchIdx = StartIdx, e = MI->getNumOperands();
|
|
|
|
while (ScratchIdx < e &&
|
|
|
|
!(MI->getOperand(ScratchIdx).isReg() &&
|
|
|
|
MI->getOperand(ScratchIdx).isDef() &&
|
|
|
|
MI->getOperand(ScratchIdx).isImplicit() &&
|
|
|
|
MI->getOperand(ScratchIdx).isEarlyClobber()))
|
|
|
|
++ScratchIdx;
|
|
|
|
|
|
|
|
assert(ScratchIdx != e && "No scratch register available");
|
|
|
|
return ScratchIdx;
|
|
|
|
}
|
|
|
|
|
[Statepoints] Change statepoint machine instr format to better suit VReg lowering.
Current Statepoint MI format is this:
STATEPOINT
<id>, <num patch bytes >, <num call arguments>, <call target>,
[call arguments...],
<StackMaps::ConstantOp>, <calling convention>,
<StackMaps::ConstantOp>, <statepoint flags>,
<StackMaps::ConstantOp>, <num deopt args>, [deopt args...],
<gc base/derived pairs...> <gc allocas...>
Note that GC pointers are listed in pairs <base,derived>.
This causes base pointers to appear many times (at least twice) in
instruction, which is bad for us when VReg lowering is ON.
The problem is that machine operand tiedness is 1-1 relation, so
it might look like this:
%vr2 = STATEPOINT ... %vr1, %vr1(tied-def0)
Since only one instance of %vr1 is tied, that may lead to incorrect
codegen (see PR46917 for more details), so we have to always spill
base pointers. This mostly defeats new VReg lowering scheme.
This patch changes statepoint instruction format so that every
gc pointer appears only once in operand list. That way they all can
be tied. Additional set of operands is added to preserve base-derived
relation required to build stackmap.
New statepoint has following format:
STATEPOINT
<id>, <num patch bytes>, <num call arguments>, <call target>,
[call arguments...],
<StackMaps::ConstantOp>, <calling convention>,
<StackMaps::ConstantOp>, <statepoint flags>,
<StackMaps::ConstantOp>, <num deopt args>, [deopt args...],
<StackMaps::ConstantOp>, <num gc pointers>, [gc pointers...],
<StackMaps::ConstantOp>, <num gc allocas>, [gc allocas...]
<StackMaps::ConstantOp>, <num entries in gc map>, [base/derived indices...]
Changes are:
- every gc pointer is listed only once in a flat length-prefixed list;
- alloca list is prefixed with its length too;
- following alloca list is length-prefixed list of base-derived
indices of pointers from gc pointer list. Note that indices are
logical (number of pointer), not absolute (index of machine operand).
Differential Revision: https://reviews.llvm.org/D87154
2020-09-04 19:45:41 +02:00
|
|
|
int StatepointOpers::getFirstGCPtrIdx() {
|
|
|
|
unsigned NumDeoptsIdx = getNumDeoptArgsIdx();
|
|
|
|
unsigned NumDeoptArgs = MI->getOperand(NumDeoptsIdx).getImm();
|
|
|
|
|
|
|
|
unsigned CurIdx = NumDeoptsIdx + 1;
|
|
|
|
while (NumDeoptArgs--) {
|
|
|
|
CurIdx = StackMaps::getNextMetaArgIdx(MI, CurIdx);
|
|
|
|
}
|
|
|
|
++CurIdx; // <StackMaps::ConstantOp>
|
|
|
|
unsigned NumGCPtrs = MI->getOperand(CurIdx).getImm();
|
|
|
|
if (NumGCPtrs == 0)
|
|
|
|
return -1;
|
|
|
|
++CurIdx; // <num gc ptrs>
|
|
|
|
assert(CurIdx < MI->getNumOperands() && "Index points past operand list");
|
|
|
|
return (int)CurIdx;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned StatepointOpers::getGCPointerMap(
|
|
|
|
SmallVectorImpl<std::pair<unsigned, unsigned>> &GCMap) {
|
|
|
|
int FirstGCIdx = getFirstGCPtrIdx();
|
|
|
|
if (FirstGCIdx == -1)
|
|
|
|
return 0;
|
|
|
|
unsigned NumGCPtr = getConstMetaVal(*MI, (unsigned)FirstGCIdx - 2);
|
|
|
|
unsigned CurIdx = (unsigned)FirstGCIdx;
|
|
|
|
while (NumGCPtr--)
|
|
|
|
CurIdx = StackMaps::getNextMetaArgIdx(MI, CurIdx);
|
|
|
|
|
|
|
|
unsigned NumAllocas = getConstMetaVal(*MI, CurIdx);
|
|
|
|
CurIdx += 2;
|
|
|
|
while (NumAllocas--)
|
|
|
|
CurIdx = StackMaps::getNextMetaArgIdx(MI, CurIdx);
|
|
|
|
|
|
|
|
assert(CurIdx < MI->getNumOperands());
|
|
|
|
unsigned GCMapSize = getConstMetaVal(*MI, CurIdx);
|
|
|
|
CurIdx += 2;
|
|
|
|
for (unsigned N = 0; N < GCMapSize; ++N) {
|
|
|
|
unsigned B = MI->getOperand(CurIdx++).getImm();
|
|
|
|
unsigned D = MI->getOperand(CurIdx++).getImm();
|
|
|
|
GCMap.push_back(std::make_pair(B, D));
|
|
|
|
}
|
|
|
|
|
|
|
|
return GCMapSize;
|
|
|
|
}
|
|
|
|
|
2014-05-02 00:21:30 +02:00
|
|
|
StackMaps::StackMaps(AsmPrinter &AP) : AP(AP) {
|
2017-04-28 06:48:42 +02:00
|
|
|
if (StackMapVersion != 3)
|
2014-05-02 00:21:30 +02:00
|
|
|
llvm_unreachable("Unsupported stackmap version!");
|
|
|
|
}
|
|
|
|
|
[Statepoints] Change statepoint machine instr format to better suit VReg lowering.
Current Statepoint MI format is this:
STATEPOINT
<id>, <num patch bytes >, <num call arguments>, <call target>,
[call arguments...],
<StackMaps::ConstantOp>, <calling convention>,
<StackMaps::ConstantOp>, <statepoint flags>,
<StackMaps::ConstantOp>, <num deopt args>, [deopt args...],
<gc base/derived pairs...> <gc allocas...>
Note that GC pointers are listed in pairs <base,derived>.
This causes base pointers to appear many times (at least twice) in
instruction, which is bad for us when VReg lowering is ON.
The problem is that machine operand tiedness is 1-1 relation, so
it might look like this:
%vr2 = STATEPOINT ... %vr1, %vr1(tied-def0)
Since only one instance of %vr1 is tied, that may lead to incorrect
codegen (see PR46917 for more details), so we have to always spill
base pointers. This mostly defeats new VReg lowering scheme.
This patch changes statepoint instruction format so that every
gc pointer appears only once in operand list. That way they all can
be tied. Additional set of operands is added to preserve base-derived
relation required to build stackmap.
New statepoint has following format:
STATEPOINT
<id>, <num patch bytes>, <num call arguments>, <call target>,
[call arguments...],
<StackMaps::ConstantOp>, <calling convention>,
<StackMaps::ConstantOp>, <statepoint flags>,
<StackMaps::ConstantOp>, <num deopt args>, [deopt args...],
<StackMaps::ConstantOp>, <num gc pointers>, [gc pointers...],
<StackMaps::ConstantOp>, <num gc allocas>, [gc allocas...]
<StackMaps::ConstantOp>, <num entries in gc map>, [base/derived indices...]
Changes are:
- every gc pointer is listed only once in a flat length-prefixed list;
- alloca list is prefixed with its length too;
- following alloca list is length-prefixed list of base-derived
indices of pointers from gc pointer list. Note that indices are
logical (number of pointer), not absolute (index of machine operand).
Differential Revision: https://reviews.llvm.org/D87154
2020-09-04 19:45:41 +02:00
|
|
|
unsigned StackMaps::getNextMetaArgIdx(const MachineInstr *MI, unsigned CurIdx) {
|
2020-09-07 17:04:07 +02:00
|
|
|
assert(CurIdx < MI->getNumOperands() && "Bad meta arg index");
|
|
|
|
const auto &MO = MI->getOperand(CurIdx);
|
|
|
|
if (MO.isImm()) {
|
|
|
|
switch (MO.getImm()) {
|
|
|
|
default:
|
|
|
|
llvm_unreachable("Unrecognized operand type.");
|
|
|
|
case StackMaps::DirectMemRefOp:
|
|
|
|
CurIdx += 2;
|
|
|
|
break;
|
|
|
|
case StackMaps::IndirectMemRefOp:
|
|
|
|
CurIdx += 3;
|
|
|
|
break;
|
|
|
|
case StackMaps::ConstantOp:
|
|
|
|
++CurIdx;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
++CurIdx;
|
|
|
|
assert(CurIdx < MI->getNumOperands() && "points past operand list");
|
|
|
|
return CurIdx;
|
|
|
|
}
|
|
|
|
|
2015-03-20 17:03:42 +01:00
|
|
|
/// Go up the super-register chain until we hit a valid dwarf register number.
|
|
|
|
static unsigned getDwarfRegNum(unsigned Reg, const TargetRegisterInfo *TRI) {
|
2015-07-09 19:11:11 +02:00
|
|
|
int RegNum = TRI->getDwarfRegNum(Reg, false);
|
|
|
|
for (MCSuperRegIterator SR(Reg, TRI); SR.isValid() && RegNum < 0; ++SR)
|
|
|
|
RegNum = TRI->getDwarfRegNum(*SR, false);
|
2015-03-20 17:03:42 +01:00
|
|
|
|
2015-07-09 19:11:11 +02:00
|
|
|
assert(RegNum >= 0 && "Invalid Dwarf register number.");
|
|
|
|
return (unsigned)RegNum;
|
2015-03-20 17:03:42 +01:00
|
|
|
}
|
|
|
|
|
2013-12-15 00:06:19 +01:00
|
|
|
MachineInstr::const_mop_iterator
|
2013-11-29 04:07:54 +01:00
|
|
|
StackMaps::parseOperand(MachineInstr::const_mop_iterator MOI,
|
2015-07-09 00:42:09 +02:00
|
|
|
MachineInstr::const_mop_iterator MOE, LocationVec &Locs,
|
|
|
|
LiveOutVec &LiveOuts) const {
|
2015-03-20 17:03:42 +01:00
|
|
|
const TargetRegisterInfo *TRI = AP.MF->getSubtarget().getRegisterInfo();
|
2013-12-15 00:06:19 +01:00
|
|
|
if (MOI->isImm()) {
|
|
|
|
switch (MOI->getImm()) {
|
2015-07-09 00:42:09 +02:00
|
|
|
default:
|
|
|
|
llvm_unreachable("Unrecognized operand type.");
|
2013-12-15 00:06:19 +01:00
|
|
|
case StackMaps::DirectMemRefOp: {
|
2015-07-16 08:11:10 +02:00
|
|
|
auto &DL = AP.MF->getDataLayout();
|
|
|
|
|
|
|
|
unsigned Size = DL.getPointerSizeInBits();
|
2013-12-15 00:06:19 +01:00
|
|
|
assert((Size % 8) == 0 && "Need pointer size in bytes.");
|
|
|
|
Size /= 8;
|
Apply llvm-prefer-register-over-unsigned from clang-tidy to LLVM
Summary:
This clang-tidy check is looking for unsigned integer variables whose initializer
starts with an implicit cast from llvm::Register and changes the type of the
variable to llvm::Register (dropping the llvm:: where possible).
Partial reverts in:
X86FrameLowering.cpp - Some functions return unsigned and arguably should be MCRegister
X86FixupLEAs.cpp - Some functions return unsigned and arguably should be MCRegister
X86FrameLowering.cpp - Some functions return unsigned and arguably should be MCRegister
HexagonBitSimplify.cpp - Function takes BitTracker::RegisterRef which appears to be unsigned&
MachineVerifier.cpp - Ambiguous operator==() given MCRegister and const Register
PPCFastISel.cpp - No Register::operator-=()
PeepholeOptimizer.cpp - TargetInstrInfo::optimizeLoadInstr() takes an unsigned&
MachineTraceMetrics.cpp - MachineTraceMetrics lacks a suitable constructor
Manual fixups in:
ARMFastISel.cpp - ARMEmitLoad() now takes a Register& instead of unsigned&
HexagonSplitDouble.cpp - Ternary operator was ambiguous between unsigned/Register
HexagonConstExtenders.cpp - Has a local class named Register, used llvm::Register instead of Register.
PPCFastISel.cpp - PPCEmitLoad() now takes a Register& instead of unsigned&
Depends on D65919
Reviewers: arsenm, bogner, craig.topper, RKSimon
Reviewed By: arsenm
Subscribers: RKSimon, craig.topper, lenary, aemerson, wuzish, jholewinski, MatzeB, qcolombet, dschuff, jyknight, dylanmckay, sdardis, nemanjai, jvesely, wdng, nhaehnle, sbc100, jgravelle-google, kristof.beyls, hiraditya, aheejin, kbarton, fedor.sergeev, javed.absar, asb, rbar, johnrusso, simoncook, apazos, sabuasal, niosHD, jrtc27, MaskRay, zzheng, edward-jones, atanasyan, rogfer01, MartinMosbeck, brucehoult, the_o, tpr, PkmX, jocewei, jsji, Petar.Avramovic, asbirlea, Jim, s.egerton, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D65962
llvm-svn: 369041
2019-08-15 21:22:08 +02:00
|
|
|
Register Reg = (++MOI)->getReg();
|
2013-12-15 00:06:19 +01:00
|
|
|
int64_t Imm = (++MOI)->getImm();
|
2015-07-09 19:11:08 +02:00
|
|
|
Locs.emplace_back(StackMaps::Location::Direct, Size,
|
|
|
|
getDwarfRegNum(Reg, TRI), Imm);
|
2013-12-15 00:06:19 +01:00
|
|
|
break;
|
2013-11-29 04:07:54 +01:00
|
|
|
}
|
2013-12-15 00:06:19 +01:00
|
|
|
case StackMaps::IndirectMemRefOp: {
|
|
|
|
int64_t Size = (++MOI)->getImm();
|
|
|
|
assert(Size > 0 && "Need a valid size for indirect memory locations.");
|
Apply llvm-prefer-register-over-unsigned from clang-tidy to LLVM
Summary:
This clang-tidy check is looking for unsigned integer variables whose initializer
starts with an implicit cast from llvm::Register and changes the type of the
variable to llvm::Register (dropping the llvm:: where possible).
Partial reverts in:
X86FrameLowering.cpp - Some functions return unsigned and arguably should be MCRegister
X86FixupLEAs.cpp - Some functions return unsigned and arguably should be MCRegister
X86FrameLowering.cpp - Some functions return unsigned and arguably should be MCRegister
HexagonBitSimplify.cpp - Function takes BitTracker::RegisterRef which appears to be unsigned&
MachineVerifier.cpp - Ambiguous operator==() given MCRegister and const Register
PPCFastISel.cpp - No Register::operator-=()
PeepholeOptimizer.cpp - TargetInstrInfo::optimizeLoadInstr() takes an unsigned&
MachineTraceMetrics.cpp - MachineTraceMetrics lacks a suitable constructor
Manual fixups in:
ARMFastISel.cpp - ARMEmitLoad() now takes a Register& instead of unsigned&
HexagonSplitDouble.cpp - Ternary operator was ambiguous between unsigned/Register
HexagonConstExtenders.cpp - Has a local class named Register, used llvm::Register instead of Register.
PPCFastISel.cpp - PPCEmitLoad() now takes a Register& instead of unsigned&
Depends on D65919
Reviewers: arsenm, bogner, craig.topper, RKSimon
Reviewed By: arsenm
Subscribers: RKSimon, craig.topper, lenary, aemerson, wuzish, jholewinski, MatzeB, qcolombet, dschuff, jyknight, dylanmckay, sdardis, nemanjai, jvesely, wdng, nhaehnle, sbc100, jgravelle-google, kristof.beyls, hiraditya, aheejin, kbarton, fedor.sergeev, javed.absar, asb, rbar, johnrusso, simoncook, apazos, sabuasal, niosHD, jrtc27, MaskRay, zzheng, edward-jones, atanasyan, rogfer01, MartinMosbeck, brucehoult, the_o, tpr, PkmX, jocewei, jsji, Petar.Avramovic, asbirlea, Jim, s.egerton, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D65962
llvm-svn: 369041
2019-08-15 21:22:08 +02:00
|
|
|
Register Reg = (++MOI)->getReg();
|
2013-12-15 00:06:19 +01:00
|
|
|
int64_t Imm = (++MOI)->getImm();
|
2015-07-09 19:11:08 +02:00
|
|
|
Locs.emplace_back(StackMaps::Location::Indirect, Size,
|
|
|
|
getDwarfRegNum(Reg, TRI), Imm);
|
2013-12-15 00:06:19 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case StackMaps::ConstantOp: {
|
|
|
|
++MOI;
|
|
|
|
assert(MOI->isImm() && "Expected constant operand.");
|
|
|
|
int64_t Imm = MOI->getImm();
|
2015-07-09 19:11:08 +02:00
|
|
|
Locs.emplace_back(Location::Constant, sizeof(int64_t), 0, Imm);
|
2013-12-15 00:06:19 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ++MOI;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The physical register number will ultimately be encoded as a DWARF regno.
|
|
|
|
// The stack map also records the size of a spill slot that can hold the
|
|
|
|
// register content. (The runtime can track the actual size of the data type
|
|
|
|
// if it needs to.)
|
|
|
|
if (MOI->isReg()) {
|
|
|
|
// Skip implicit registers (this includes our scratch registers)
|
|
|
|
if (MOI->isImplicit())
|
|
|
|
return ++MOI;
|
|
|
|
|
2019-08-02 01:27:28 +02:00
|
|
|
assert(Register::isPhysicalRegister(MOI->getReg()) &&
|
2013-12-15 00:06:19 +01:00
|
|
|
"Virtreg operands should have been rewritten before now.");
|
2015-03-20 17:03:42 +01:00
|
|
|
const TargetRegisterClass *RC = TRI->getMinimalPhysRegClass(MOI->getReg());
|
2013-12-15 00:06:19 +01:00
|
|
|
assert(!MOI->getSubReg() && "Physical subreg still around.");
|
2015-03-20 17:03:42 +01:00
|
|
|
|
|
|
|
unsigned Offset = 0;
|
2015-07-09 19:11:11 +02:00
|
|
|
unsigned DwarfRegNum = getDwarfRegNum(MOI->getReg(), TRI);
|
MCRegisterInfo: Merge getLLVMRegNum and getLLVMRegNumFromEH
Summary:
The functions different in two ways:
- getLLVMRegNum could return both "eh" and "other" dwarf register
numbers, while getLLVMRegNumFromEH only returned the "eh" number.
- getLLVMRegNum asserted if the register was not found, while the second
function returned -1.
The second distinction was pretty important, but it was very hard to
infer that from the function name. Aditionally, for the use case of
dumping dwarf expressions, we needed a function which can work with both
kinds of number, but does not assert.
This patch solves both of these issues by merging the two functions into
one, returning an Optional<unsigned> value. While the same thing could
be achieved by adding an "IsEH" argument to the (renamed)
getLLVMRegNumFromEH function, it seemed better to avoid the confusion of
two functions and put the choice of asserting into the hands of the
caller -- if he checks the Optional value, he can safely process
"untrusted" input, and if he blindly dereferences the Optional, he gets
the assertion.
I've updated all call sites to the new API, choosing between the two
options according to the function they were calling originally, except
that I've updated the usage in DWARFExpression.cpp to use the "safe"
method instead, and added a test case which would have previously
triggered an assertion failure when processing (incorrect?) dwarf
expressions.
Reviewers: dsanders, arsenm, JDevlieghere
Subscribers: wdng, aprantl, javed.absar, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D67154
llvm-svn: 372710
2019-09-24 11:31:02 +02:00
|
|
|
unsigned LLVMRegNum = *TRI->getLLVMRegNum(DwarfRegNum, false);
|
2015-07-09 19:11:11 +02:00
|
|
|
unsigned SubRegIdx = TRI->getSubRegIndex(LLVMRegNum, MOI->getReg());
|
2015-03-20 17:03:42 +01:00
|
|
|
if (SubRegIdx)
|
|
|
|
Offset = TRI->getSubRegIdxOffset(SubRegIdx);
|
|
|
|
|
2017-04-24 20:55:33 +02:00
|
|
|
Locs.emplace_back(Location::Register, TRI->getSpillSize(*RC),
|
|
|
|
DwarfRegNum, Offset);
|
2013-12-15 00:06:19 +01:00
|
|
|
return ++MOI;
|
2013-11-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-12-15 00:06:19 +01:00
|
|
|
if (MOI->isRegLiveOut())
|
|
|
|
LiveOuts = parseRegisterLiveOutMask(MOI->getRegLiveOut());
|
|
|
|
|
|
|
|
return ++MOI;
|
2013-11-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2015-03-20 17:03:42 +01:00
|
|
|
void StackMaps::print(raw_ostream &OS) {
|
|
|
|
const TargetRegisterInfo *TRI =
|
|
|
|
AP.MF ? AP.MF->getSubtarget().getRegisterInfo() : nullptr;
|
|
|
|
OS << WSMP << "callsites:\n";
|
|
|
|
for (const auto &CSI : CSInfos) {
|
|
|
|
const LocationVec &CSLocs = CSI.Locations;
|
|
|
|
const LiveOutVec &LiveOuts = CSI.LiveOuts;
|
2013-12-14 07:53:06 +01:00
|
|
|
|
2015-03-20 17:03:42 +01:00
|
|
|
OS << WSMP << "callsite " << CSI.ID << "\n";
|
|
|
|
OS << WSMP << " has " << CSLocs.size() << " locations\n";
|
|
|
|
|
2015-07-09 19:11:11 +02:00
|
|
|
unsigned Idx = 0;
|
2015-03-20 17:03:42 +01:00
|
|
|
for (const auto &Loc : CSLocs) {
|
2015-07-09 19:11:11 +02:00
|
|
|
OS << WSMP << "\t\tLoc " << Idx << ": ";
|
|
|
|
switch (Loc.Type) {
|
2015-03-20 17:03:42 +01:00
|
|
|
case Location::Unprocessed:
|
|
|
|
OS << "<Unprocessed operand>";
|
|
|
|
break;
|
|
|
|
case Location::Register:
|
|
|
|
OS << "Register ";
|
2015-07-09 00:42:09 +02:00
|
|
|
if (TRI)
|
2017-11-30 17:12:24 +01:00
|
|
|
OS << printReg(Loc.Reg, TRI);
|
2015-07-09 00:42:09 +02:00
|
|
|
else
|
|
|
|
OS << Loc.Reg;
|
2015-03-20 17:03:42 +01:00
|
|
|
break;
|
|
|
|
case Location::Direct:
|
|
|
|
OS << "Direct ";
|
|
|
|
if (TRI)
|
2017-11-30 17:12:24 +01:00
|
|
|
OS << printReg(Loc.Reg, TRI);
|
2015-03-20 17:03:42 +01:00
|
|
|
else
|
|
|
|
OS << Loc.Reg;
|
|
|
|
if (Loc.Offset)
|
|
|
|
OS << " + " << Loc.Offset;
|
|
|
|
break;
|
|
|
|
case Location::Indirect:
|
|
|
|
OS << "Indirect ";
|
|
|
|
if (TRI)
|
2017-11-30 17:12:24 +01:00
|
|
|
OS << printReg(Loc.Reg, TRI);
|
2015-03-20 17:03:42 +01:00
|
|
|
else
|
|
|
|
OS << Loc.Reg;
|
|
|
|
OS << "+" << Loc.Offset;
|
|
|
|
break;
|
|
|
|
case Location::Constant:
|
|
|
|
OS << "Constant " << Loc.Offset;
|
|
|
|
break;
|
|
|
|
case Location::ConstantIndex:
|
|
|
|
OS << "Constant Index " << Loc.Offset;
|
|
|
|
break;
|
|
|
|
}
|
2017-04-28 06:48:42 +02:00
|
|
|
OS << "\t[encoding: .byte " << Loc.Type << ", .byte 0"
|
|
|
|
<< ", .short " << Loc.Size << ", .short " << Loc.Reg << ", .short 0"
|
|
|
|
<< ", .int " << Loc.Offset << "]\n";
|
2015-07-09 19:11:11 +02:00
|
|
|
Idx++;
|
2015-03-20 17:03:42 +01:00
|
|
|
}
|
|
|
|
|
2015-07-09 19:11:11 +02:00
|
|
|
OS << WSMP << "\thas " << LiveOuts.size() << " live-out registers\n";
|
2015-03-20 17:03:42 +01:00
|
|
|
|
2015-07-09 19:11:11 +02:00
|
|
|
Idx = 0;
|
2015-03-20 17:03:42 +01:00
|
|
|
for (const auto &LO : LiveOuts) {
|
2015-07-09 19:11:11 +02:00
|
|
|
OS << WSMP << "\t\tLO " << Idx << ": ";
|
2015-03-20 17:03:42 +01:00
|
|
|
if (TRI)
|
2017-11-30 17:12:24 +01:00
|
|
|
OS << printReg(LO.Reg, TRI);
|
2015-03-20 17:03:42 +01:00
|
|
|
else
|
|
|
|
OS << LO.Reg;
|
2015-07-09 19:11:11 +02:00
|
|
|
OS << "\t[encoding: .short " << LO.DwarfRegNum << ", .byte 0, .byte "
|
2015-03-20 17:03:42 +01:00
|
|
|
<< LO.Size << "]\n";
|
2015-07-09 19:11:11 +02:00
|
|
|
Idx++;
|
2015-03-20 17:03:42 +01:00
|
|
|
}
|
|
|
|
}
|
2013-12-14 07:53:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Create a live-out register record for the given register Reg.
|
|
|
|
StackMaps::LiveOutReg
|
2014-02-11 00:30:26 +01:00
|
|
|
StackMaps::createLiveOutReg(unsigned Reg, const TargetRegisterInfo *TRI) const {
|
2015-07-09 19:11:11 +02:00
|
|
|
unsigned DwarfRegNum = getDwarfRegNum(Reg, TRI);
|
2017-04-24 20:55:33 +02:00
|
|
|
unsigned Size = TRI->getSpillSize(*TRI->getMinimalPhysRegClass(Reg));
|
2015-07-09 19:11:11 +02:00
|
|
|
return LiveOutReg(Reg, DwarfRegNum, Size);
|
2013-12-14 07:53:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse the register live-out mask and return a vector of live-out registers
|
|
|
|
/// that need to be recorded in the stackmap.
|
|
|
|
StackMaps::LiveOutVec
|
|
|
|
StackMaps::parseRegisterLiveOutMask(const uint32_t *Mask) const {
|
|
|
|
assert(Mask && "No register mask specified");
|
2015-03-20 00:27:42 +01:00
|
|
|
const TargetRegisterInfo *TRI = AP.MF->getSubtarget().getRegisterInfo();
|
2013-12-14 07:53:06 +01:00
|
|
|
LiveOutVec LiveOuts;
|
|
|
|
|
|
|
|
// Create a LiveOutReg for each bit that is set in the register mask.
|
|
|
|
for (unsigned Reg = 0, NumRegs = TRI->getNumRegs(); Reg != NumRegs; ++Reg)
|
2019-11-09 17:37:20 +01:00
|
|
|
if ((Mask[Reg / 32] >> (Reg % 32)) & 1)
|
2014-02-11 00:30:26 +01:00
|
|
|
LiveOuts.push_back(createLiveOutReg(Reg, TRI));
|
2013-12-14 07:53:06 +01:00
|
|
|
|
|
|
|
// We don't need to keep track of a register if its super-register is already
|
|
|
|
// in the list. Merge entries that refer to the same dwarf register and use
|
|
|
|
// the maximum size that needs to be spilled.
|
2015-07-09 19:11:15 +02:00
|
|
|
|
llvm::sort(C.begin(), C.end(), ...) -> llvm::sort(C, ...)
Summary: The convenience wrapper in STLExtras is available since rL342102.
Reviewers: dblaikie, javed.absar, JDevlieghere, andreadb
Subscribers: MatzeB, sanjoy, arsenm, dschuff, mehdi_amini, sdardis, nemanjai, jvesely, nhaehnle, sbc100, jgravelle-google, eraman, aheejin, kbarton, JDevlieghere, javed.absar, gbedwell, jrtc27, mgrang, atanasyan, steven_wu, george.burgess.iv, dexonsmith, kristina, jsji, llvm-commits
Differential Revision: https://reviews.llvm.org/D52573
llvm-svn: 343163
2018-09-27 04:13:45 +02:00
|
|
|
llvm::sort(LiveOuts, [](const LiveOutReg &LHS, const LiveOutReg &RHS) {
|
|
|
|
// Only sort by the dwarf register number.
|
|
|
|
return LHS.DwarfRegNum < RHS.DwarfRegNum;
|
|
|
|
});
|
2015-07-09 19:11:15 +02:00
|
|
|
|
|
|
|
for (auto I = LiveOuts.begin(), E = LiveOuts.end(); I != E; ++I) {
|
|
|
|
for (auto II = std::next(I); II != E; ++II) {
|
2015-07-09 19:11:11 +02:00
|
|
|
if (I->DwarfRegNum != II->DwarfRegNum) {
|
2013-12-14 07:53:06 +01:00
|
|
|
// Skip all the now invalid entries.
|
|
|
|
I = --II;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
I->Size = std::max(I->Size, II->Size);
|
|
|
|
if (TRI->isSuperRegister(I->Reg, II->Reg))
|
|
|
|
I->Reg = II->Reg;
|
2015-07-09 19:11:15 +02:00
|
|
|
II->Reg = 0; // mark for deletion.
|
2013-12-14 07:53:06 +01:00
|
|
|
}
|
|
|
|
}
|
2015-07-09 19:11:15 +02:00
|
|
|
|
2015-07-09 00:42:09 +02:00
|
|
|
LiveOuts.erase(
|
2017-02-22 23:32:51 +01:00
|
|
|
llvm::remove_if(LiveOuts,
|
|
|
|
[](const LiveOutReg &LO) { return LO.Reg == 0; }),
|
2015-07-09 00:42:09 +02:00
|
|
|
LiveOuts.end());
|
2015-07-09 19:11:15 +02:00
|
|
|
|
2013-12-14 07:53:06 +01:00
|
|
|
return LiveOuts;
|
|
|
|
}
|
|
|
|
|
[Statepoints] Change statepoint machine instr format to better suit VReg lowering.
Current Statepoint MI format is this:
STATEPOINT
<id>, <num patch bytes >, <num call arguments>, <call target>,
[call arguments...],
<StackMaps::ConstantOp>, <calling convention>,
<StackMaps::ConstantOp>, <statepoint flags>,
<StackMaps::ConstantOp>, <num deopt args>, [deopt args...],
<gc base/derived pairs...> <gc allocas...>
Note that GC pointers are listed in pairs <base,derived>.
This causes base pointers to appear many times (at least twice) in
instruction, which is bad for us when VReg lowering is ON.
The problem is that machine operand tiedness is 1-1 relation, so
it might look like this:
%vr2 = STATEPOINT ... %vr1, %vr1(tied-def0)
Since only one instance of %vr1 is tied, that may lead to incorrect
codegen (see PR46917 for more details), so we have to always spill
base pointers. This mostly defeats new VReg lowering scheme.
This patch changes statepoint instruction format so that every
gc pointer appears only once in operand list. That way they all can
be tied. Additional set of operands is added to preserve base-derived
relation required to build stackmap.
New statepoint has following format:
STATEPOINT
<id>, <num patch bytes>, <num call arguments>, <call target>,
[call arguments...],
<StackMaps::ConstantOp>, <calling convention>,
<StackMaps::ConstantOp>, <statepoint flags>,
<StackMaps::ConstantOp>, <num deopt args>, [deopt args...],
<StackMaps::ConstantOp>, <num gc pointers>, [gc pointers...],
<StackMaps::ConstantOp>, <num gc allocas>, [gc allocas...]
<StackMaps::ConstantOp>, <num entries in gc map>, [base/derived indices...]
Changes are:
- every gc pointer is listed only once in a flat length-prefixed list;
- alloca list is prefixed with its length too;
- following alloca list is length-prefixed list of base-derived
indices of pointers from gc pointer list. Note that indices are
logical (number of pointer), not absolute (index of machine operand).
Differential Revision: https://reviews.llvm.org/D87154
2020-09-04 19:45:41 +02:00
|
|
|
// See statepoint MI format description in StatepointOpers' class comment
|
|
|
|
// in include/llvm/CodeGen/StackMaps.h
|
|
|
|
void StackMaps::parseStatepointOpers(const MachineInstr &MI,
|
|
|
|
MachineInstr::const_mop_iterator MOI,
|
|
|
|
MachineInstr::const_mop_iterator MOE,
|
|
|
|
LocationVec &Locations,
|
|
|
|
LiveOutVec &LiveOuts) {
|
|
|
|
LLVM_DEBUG(dbgs() << "record statepoint : " << MI << "\n");
|
|
|
|
StatepointOpers SO(&MI);
|
|
|
|
MOI = parseOperand(MOI, MOE, Locations, LiveOuts); // CC
|
|
|
|
MOI = parseOperand(MOI, MOE, Locations, LiveOuts); // Flags
|
|
|
|
MOI = parseOperand(MOI, MOE, Locations, LiveOuts); // Num Deopts
|
|
|
|
|
|
|
|
// Record Deopt Args.
|
|
|
|
unsigned NumDeoptArgs = Locations.back().Offset;
|
|
|
|
assert(Locations.back().Type = Location::Constant);
|
|
|
|
assert(NumDeoptArgs == SO.getNumDeoptArgs());
|
|
|
|
|
|
|
|
while (NumDeoptArgs--)
|
|
|
|
MOI = parseOperand(MOI, MOE, Locations, LiveOuts);
|
|
|
|
|
|
|
|
// Record gc base/derived pairs
|
|
|
|
assert(MOI->isImm() && MOI->getImm() == StackMaps::ConstantOp);
|
|
|
|
++MOI;
|
|
|
|
assert(MOI->isImm());
|
|
|
|
unsigned NumGCPointers = MOI->getImm();
|
|
|
|
++MOI;
|
|
|
|
if (NumGCPointers) {
|
|
|
|
// Map logical index of GC ptr to MI operand index.
|
|
|
|
SmallVector<unsigned, 8> GCPtrIndices;
|
|
|
|
unsigned GCPtrIdx = (unsigned)SO.getFirstGCPtrIdx();
|
|
|
|
assert((int)GCPtrIdx != -1);
|
|
|
|
assert(MOI - MI.operands_begin() == GCPtrIdx);
|
|
|
|
while (NumGCPointers--) {
|
|
|
|
GCPtrIndices.push_back(GCPtrIdx);
|
|
|
|
GCPtrIdx = StackMaps::getNextMetaArgIdx(&MI, GCPtrIdx);
|
|
|
|
}
|
|
|
|
|
|
|
|
SmallVector<std::pair<unsigned, unsigned>, 8> GCPairs;
|
|
|
|
unsigned NumGCPairs = SO.getGCPointerMap(GCPairs);
|
2020-10-06 16:02:17 +02:00
|
|
|
(void)NumGCPairs;
|
[Statepoints] Change statepoint machine instr format to better suit VReg lowering.
Current Statepoint MI format is this:
STATEPOINT
<id>, <num patch bytes >, <num call arguments>, <call target>,
[call arguments...],
<StackMaps::ConstantOp>, <calling convention>,
<StackMaps::ConstantOp>, <statepoint flags>,
<StackMaps::ConstantOp>, <num deopt args>, [deopt args...],
<gc base/derived pairs...> <gc allocas...>
Note that GC pointers are listed in pairs <base,derived>.
This causes base pointers to appear many times (at least twice) in
instruction, which is bad for us when VReg lowering is ON.
The problem is that machine operand tiedness is 1-1 relation, so
it might look like this:
%vr2 = STATEPOINT ... %vr1, %vr1(tied-def0)
Since only one instance of %vr1 is tied, that may lead to incorrect
codegen (see PR46917 for more details), so we have to always spill
base pointers. This mostly defeats new VReg lowering scheme.
This patch changes statepoint instruction format so that every
gc pointer appears only once in operand list. That way they all can
be tied. Additional set of operands is added to preserve base-derived
relation required to build stackmap.
New statepoint has following format:
STATEPOINT
<id>, <num patch bytes>, <num call arguments>, <call target>,
[call arguments...],
<StackMaps::ConstantOp>, <calling convention>,
<StackMaps::ConstantOp>, <statepoint flags>,
<StackMaps::ConstantOp>, <num deopt args>, [deopt args...],
<StackMaps::ConstantOp>, <num gc pointers>, [gc pointers...],
<StackMaps::ConstantOp>, <num gc allocas>, [gc allocas...]
<StackMaps::ConstantOp>, <num entries in gc map>, [base/derived indices...]
Changes are:
- every gc pointer is listed only once in a flat length-prefixed list;
- alloca list is prefixed with its length too;
- following alloca list is length-prefixed list of base-derived
indices of pointers from gc pointer list. Note that indices are
logical (number of pointer), not absolute (index of machine operand).
Differential Revision: https://reviews.llvm.org/D87154
2020-09-04 19:45:41 +02:00
|
|
|
LLVM_DEBUG(dbgs() << "NumGCPairs = " << NumGCPairs << "\n");
|
|
|
|
|
|
|
|
auto MOB = MI.operands_begin();
|
|
|
|
for (auto &P : GCPairs) {
|
|
|
|
assert(P.first < GCPtrIndices.size() && "base pointer index not found");
|
|
|
|
assert(P.second < GCPtrIndices.size() &&
|
|
|
|
"derived pointer index not found");
|
|
|
|
unsigned BaseIdx = GCPtrIndices[P.first];
|
|
|
|
unsigned DerivedIdx = GCPtrIndices[P.second];
|
|
|
|
LLVM_DEBUG(dbgs() << "Base : " << BaseIdx << " Derived : " << DerivedIdx
|
|
|
|
<< "\n");
|
|
|
|
(void)parseOperand(MOB + BaseIdx, MOE, Locations, LiveOuts);
|
|
|
|
(void)parseOperand(MOB + DerivedIdx, MOE, Locations, LiveOuts);
|
|
|
|
}
|
|
|
|
|
|
|
|
MOI = MOB + GCPtrIdx;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Record gc allocas
|
|
|
|
assert(MOI < MOE);
|
|
|
|
assert(MOI->isImm() && MOI->getImm() == StackMaps::ConstantOp);
|
|
|
|
++MOI;
|
|
|
|
unsigned NumAllocas = MOI->getImm();
|
|
|
|
++MOI;
|
|
|
|
while (NumAllocas--) {
|
|
|
|
MOI = parseOperand(MOI, MOE, Locations, LiveOuts);
|
|
|
|
assert(MOI < MOE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-19 23:03:19 +01:00
|
|
|
void StackMaps::recordStackMapOpers(const MCSymbol &MILabel,
|
|
|
|
const MachineInstr &MI, uint64_t ID,
|
2013-11-19 04:29:56 +01:00
|
|
|
MachineInstr::const_mop_iterator MOI,
|
|
|
|
MachineInstr::const_mop_iterator MOE,
|
|
|
|
bool recordResult) {
|
2015-04-24 21:11:51 +02:00
|
|
|
MCContext &OutContext = AP.OutStreamer->getContext();
|
2020-02-18 03:48:38 +01:00
|
|
|
|
2013-12-14 07:53:06 +01:00
|
|
|
LocationVec Locations;
|
|
|
|
LiveOutVec LiveOuts;
|
2013-10-31 23:11:56 +01:00
|
|
|
|
2013-11-09 00:28:16 +01:00
|
|
|
if (recordResult) {
|
2013-12-15 00:06:19 +01:00
|
|
|
assert(PatchPointOpers(&MI).hasDef() && "Stackmap has no return value.");
|
2015-07-09 00:42:09 +02:00
|
|
|
parseOperand(MI.operands_begin(), std::next(MI.operands_begin()), Locations,
|
|
|
|
LiveOuts);
|
2013-11-09 00:28:16 +01:00
|
|
|
}
|
|
|
|
|
2013-12-15 00:06:19 +01:00
|
|
|
// Parse operands.
|
[Statepoints] Change statepoint machine instr format to better suit VReg lowering.
Current Statepoint MI format is this:
STATEPOINT
<id>, <num patch bytes >, <num call arguments>, <call target>,
[call arguments...],
<StackMaps::ConstantOp>, <calling convention>,
<StackMaps::ConstantOp>, <statepoint flags>,
<StackMaps::ConstantOp>, <num deopt args>, [deopt args...],
<gc base/derived pairs...> <gc allocas...>
Note that GC pointers are listed in pairs <base,derived>.
This causes base pointers to appear many times (at least twice) in
instruction, which is bad for us when VReg lowering is ON.
The problem is that machine operand tiedness is 1-1 relation, so
it might look like this:
%vr2 = STATEPOINT ... %vr1, %vr1(tied-def0)
Since only one instance of %vr1 is tied, that may lead to incorrect
codegen (see PR46917 for more details), so we have to always spill
base pointers. This mostly defeats new VReg lowering scheme.
This patch changes statepoint instruction format so that every
gc pointer appears only once in operand list. That way they all can
be tied. Additional set of operands is added to preserve base-derived
relation required to build stackmap.
New statepoint has following format:
STATEPOINT
<id>, <num patch bytes>, <num call arguments>, <call target>,
[call arguments...],
<StackMaps::ConstantOp>, <calling convention>,
<StackMaps::ConstantOp>, <statepoint flags>,
<StackMaps::ConstantOp>, <num deopt args>, [deopt args...],
<StackMaps::ConstantOp>, <num gc pointers>, [gc pointers...],
<StackMaps::ConstantOp>, <num gc allocas>, [gc allocas...]
<StackMaps::ConstantOp>, <num entries in gc map>, [base/derived indices...]
Changes are:
- every gc pointer is listed only once in a flat length-prefixed list;
- alloca list is prefixed with its length too;
- following alloca list is length-prefixed list of base-derived
indices of pointers from gc pointer list. Note that indices are
logical (number of pointer), not absolute (index of machine operand).
Differential Revision: https://reviews.llvm.org/D87154
2020-09-04 19:45:41 +02:00
|
|
|
if (MI.getOpcode() == TargetOpcode::STATEPOINT)
|
|
|
|
parseStatepointOpers(MI, MOI, MOE, Locations, LiveOuts);
|
|
|
|
else
|
|
|
|
while (MOI != MOE)
|
|
|
|
MOI = parseOperand(MOI, MOE, Locations, LiveOuts);
|
2013-10-31 23:11:56 +01:00
|
|
|
|
2013-12-15 00:06:19 +01:00
|
|
|
// Move large constants into the constant pool.
|
2015-07-09 19:11:11 +02:00
|
|
|
for (auto &Loc : Locations) {
|
2014-01-09 01:22:31 +01:00
|
|
|
// Constants are encoded as sign-extended integers.
|
|
|
|
// -1 is directly encoded as .long 0xFFFFFFFF with no constant pool.
|
2015-07-09 19:11:11 +02:00
|
|
|
if (Loc.Type == Location::Constant && !isInt<32>(Loc.Offset)) {
|
|
|
|
Loc.Type = Location::ConstantIndex;
|
2014-11-04 01:59:21 +01:00
|
|
|
// ConstPool is intentionally a MapVector of 'uint64_t's (as
|
|
|
|
// opposed to 'int64_t's). We should never be in a situation
|
|
|
|
// where we have to insert either the tombstone or the empty
|
|
|
|
// keys into a map, and for a DenseMap<uint64_t, T> these are
|
|
|
|
// (uint64_t)0 and (uint64_t)-1. They can be and are
|
|
|
|
// represented using 32 bit integers.
|
2015-07-09 19:11:11 +02:00
|
|
|
assert((uint64_t)Loc.Offset != DenseMapInfo<uint64_t>::getEmptyKey() &&
|
|
|
|
(uint64_t)Loc.Offset !=
|
|
|
|
DenseMapInfo<uint64_t>::getTombstoneKey() &&
|
2014-11-04 01:59:21 +01:00
|
|
|
"empty and tombstone keys should fit in 32 bits!");
|
2015-07-09 19:11:11 +02:00
|
|
|
auto Result = ConstPool.insert(std::make_pair(Loc.Offset, Loc.Offset));
|
|
|
|
Loc.Offset = Result.first - ConstPool.begin();
|
2013-10-31 23:11:56 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-30 19:58:27 +01:00
|
|
|
// Create an expression to calculate the offset of the callsite from function
|
|
|
|
// entry.
|
2015-05-30 03:25:56 +02:00
|
|
|
const MCExpr *CSOffsetExpr = MCBinaryExpr::createSub(
|
2019-12-19 23:03:19 +01:00
|
|
|
MCSymbolRefExpr::create(&MILabel, OutContext),
|
2015-07-09 00:42:09 +02:00
|
|
|
MCSymbolRefExpr::create(AP.CurrentFnSymForSize, OutContext), OutContext);
|
2013-10-31 23:11:56 +01:00
|
|
|
|
2014-10-04 18:55:56 +02:00
|
|
|
CSInfos.emplace_back(CSOffsetExpr, ID, std::move(Locations),
|
|
|
|
std::move(LiveOuts));
|
2014-01-30 19:58:27 +01:00
|
|
|
|
2016-09-14 22:22:03 +02:00
|
|
|
// Record the stack size of the current function and update callsite count.
|
2016-07-28 20:40:00 +02:00
|
|
|
const MachineFrameInfo &MFI = AP.MF->getFrameInfo();
|
2014-08-05 04:39:49 +02:00
|
|
|
const TargetRegisterInfo *RegInfo = AP.MF->getSubtarget().getRegisterInfo();
|
2015-07-09 19:11:11 +02:00
|
|
|
bool HasDynamicFrameSize =
|
2016-07-28 20:40:00 +02:00
|
|
|
MFI.hasVarSizedObjects() || RegInfo->needsStackRealignment(*(AP.MF));
|
2016-09-14 22:22:03 +02:00
|
|
|
uint64_t FrameSize = HasDynamicFrameSize ? UINT64_MAX : MFI.getStackSize();
|
|
|
|
|
|
|
|
auto CurrentIt = FnInfos.find(AP.CurrentFnSym);
|
|
|
|
if (CurrentIt != FnInfos.end())
|
|
|
|
CurrentIt->second.RecordCount++;
|
|
|
|
else
|
|
|
|
FnInfos.insert(std::make_pair(AP.CurrentFnSym, FunctionInfo(FrameSize)));
|
2013-10-31 23:11:56 +01:00
|
|
|
}
|
|
|
|
|
2019-12-19 23:03:19 +01:00
|
|
|
void StackMaps::recordStackMap(const MCSymbol &L, const MachineInstr &MI) {
|
2013-12-14 07:53:06 +01:00
|
|
|
assert(MI.getOpcode() == TargetOpcode::STACKMAP && "expected stackmap");
|
2013-11-19 04:29:56 +01:00
|
|
|
|
2016-08-23 23:21:43 +02:00
|
|
|
StackMapOpers opers(&MI);
|
|
|
|
const int64_t ID = MI.getOperand(PatchPointOpers::IDPos).getImm();
|
2019-12-19 23:03:19 +01:00
|
|
|
recordStackMapOpers(L, MI, ID, std::next(MI.operands_begin(),
|
|
|
|
opers.getVarIdx()),
|
2013-12-15 00:06:19 +01:00
|
|
|
MI.operands_end());
|
2013-11-19 04:29:56 +01:00
|
|
|
}
|
|
|
|
|
2019-12-19 23:03:19 +01:00
|
|
|
void StackMaps::recordPatchPoint(const MCSymbol &L, const MachineInstr &MI) {
|
2013-12-14 07:53:06 +01:00
|
|
|
assert(MI.getOpcode() == TargetOpcode::PATCHPOINT && "expected patchpoint");
|
2013-11-19 04:29:56 +01:00
|
|
|
|
|
|
|
PatchPointOpers opers(&MI);
|
2016-08-24 01:33:29 +02:00
|
|
|
const int64_t ID = opers.getID();
|
2015-07-09 19:11:11 +02:00
|
|
|
auto MOI = std::next(MI.operands_begin(), opers.getStackMapStartIdx());
|
2019-12-19 23:03:19 +01:00
|
|
|
recordStackMapOpers(L, MI, ID, MOI, MI.operands_end(),
|
2013-11-19 04:29:56 +01:00
|
|
|
opers.isAnyReg() && opers.hasDef());
|
|
|
|
|
|
|
|
#ifndef NDEBUG
|
|
|
|
// verify anyregcc
|
2015-07-09 19:11:11 +02:00
|
|
|
auto &Locations = CSInfos.back().Locations;
|
2013-11-19 04:29:56 +01:00
|
|
|
if (opers.isAnyReg()) {
|
2016-08-24 01:33:29 +02:00
|
|
|
unsigned NArgs = opers.getNumCallArgs();
|
2015-07-09 00:42:09 +02:00
|
|
|
for (unsigned i = 0, e = (opers.hasDef() ? NArgs + 1 : NArgs); i != e; ++i)
|
2015-07-09 19:11:11 +02:00
|
|
|
assert(Locations[i].Type == Location::Register &&
|
2013-11-19 04:29:56 +01:00
|
|
|
"anyreg arg must be in reg.");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
2017-02-22 23:32:51 +01:00
|
|
|
|
2019-12-19 23:03:19 +01:00
|
|
|
void StackMaps::recordStatepoint(const MCSymbol &L, const MachineInstr &MI) {
|
2015-07-09 00:42:09 +02:00
|
|
|
assert(MI.getOpcode() == TargetOpcode::STATEPOINT && "expected statepoint");
|
2014-12-01 23:52:56 +01:00
|
|
|
|
|
|
|
StatepointOpers opers(&MI);
|
|
|
|
const unsigned StartIdx = opers.getVarIdx();
|
2019-12-19 23:03:19 +01:00
|
|
|
recordStackMapOpers(L, MI, opers.getID(), MI.operands_begin() + StartIdx,
|
2015-05-13 01:52:24 +02:00
|
|
|
MI.operands_end(), false);
|
2014-12-01 23:52:56 +01:00
|
|
|
}
|
2013-11-19 04:29:56 +01:00
|
|
|
|
2014-05-02 00:21:27 +02:00
|
|
|
/// Emit the stackmap header.
|
2013-10-31 23:11:56 +01:00
|
|
|
///
|
2014-04-01 00:14:04 +02:00
|
|
|
/// Header {
|
2020-08-04 14:19:36 +02:00
|
|
|
/// uint8 : Stack Map Version (currently 3)
|
2014-04-01 00:14:04 +02:00
|
|
|
/// uint8 : Reserved (expected to be 0)
|
|
|
|
/// uint16 : Reserved (expected to be 0)
|
|
|
|
/// }
|
2014-01-30 19:58:27 +01:00
|
|
|
/// uint32 : NumFunctions
|
2014-04-01 00:14:04 +02:00
|
|
|
/// uint32 : NumConstants
|
|
|
|
/// uint32 : NumRecords
|
2014-05-02 00:21:27 +02:00
|
|
|
void StackMaps::emitStackmapHeader(MCStreamer &OS) {
|
|
|
|
// Header.
|
2020-02-15 07:40:47 +01:00
|
|
|
OS.emitIntValue(StackMapVersion, 1); // Version.
|
|
|
|
OS.emitIntValue(0, 1); // Reserved.
|
2020-02-29 17:25:22 +01:00
|
|
|
OS.emitInt16(0); // Reserved.
|
2014-05-02 00:21:27 +02:00
|
|
|
|
|
|
|
// Num functions.
|
2018-05-14 14:53:11 +02:00
|
|
|
LLVM_DEBUG(dbgs() << WSMP << "#functions = " << FnInfos.size() << '\n');
|
2020-02-29 17:25:22 +01:00
|
|
|
OS.emitInt32(FnInfos.size());
|
2014-05-02 00:21:27 +02:00
|
|
|
// Num constants.
|
2018-05-14 14:53:11 +02:00
|
|
|
LLVM_DEBUG(dbgs() << WSMP << "#constants = " << ConstPool.size() << '\n');
|
2020-02-29 17:25:22 +01:00
|
|
|
OS.emitInt32(ConstPool.size());
|
2014-05-02 00:21:27 +02:00
|
|
|
// Num callsites.
|
2018-05-14 14:53:11 +02:00
|
|
|
LLVM_DEBUG(dbgs() << WSMP << "#callsites = " << CSInfos.size() << '\n');
|
2020-02-29 17:25:22 +01:00
|
|
|
OS.emitInt32(CSInfos.size());
|
2014-05-02 00:21:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Emit the function frame record for each function.
|
|
|
|
///
|
2014-01-30 19:58:27 +01:00
|
|
|
/// StkSizeRecord[NumFunctions] {
|
2014-04-01 00:14:04 +02:00
|
|
|
/// uint64 : Function Address
|
|
|
|
/// uint64 : Stack Size
|
2016-09-14 22:22:03 +02:00
|
|
|
/// uint64 : Record Count
|
2014-01-30 19:58:27 +01:00
|
|
|
/// }
|
2014-05-02 00:21:27 +02:00
|
|
|
void StackMaps::emitFunctionFrameRecords(MCStreamer &OS) {
|
|
|
|
// Function Frame records.
|
2018-05-14 14:53:11 +02:00
|
|
|
LLVM_DEBUG(dbgs() << WSMP << "functions:\n");
|
2016-09-14 22:22:03 +02:00
|
|
|
for (auto const &FR : FnInfos) {
|
2018-05-14 14:53:11 +02:00
|
|
|
LLVM_DEBUG(dbgs() << WSMP << "function addr: " << FR.first
|
|
|
|
<< " frame size: " << FR.second.StackSize
|
|
|
|
<< " callsite count: " << FR.second.RecordCount << '\n');
|
2020-02-15 04:21:58 +01:00
|
|
|
OS.emitSymbolValue(FR.first, 8);
|
2020-02-15 07:40:47 +01:00
|
|
|
OS.emitIntValue(FR.second.StackSize, 8);
|
|
|
|
OS.emitIntValue(FR.second.RecordCount, 8);
|
2014-05-02 00:21:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Emit the constant pool.
|
|
|
|
///
|
2013-10-31 23:11:56 +01:00
|
|
|
/// int64 : Constants[NumConstants]
|
2014-05-02 00:21:27 +02:00
|
|
|
void StackMaps::emitConstantPoolEntries(MCStreamer &OS) {
|
|
|
|
// Constant pool entries.
|
2018-05-14 14:53:11 +02:00
|
|
|
LLVM_DEBUG(dbgs() << WSMP << "constants:\n");
|
2015-07-09 19:11:11 +02:00
|
|
|
for (const auto &ConstEntry : ConstPool) {
|
2018-05-14 14:53:11 +02:00
|
|
|
LLVM_DEBUG(dbgs() << WSMP << ConstEntry.second << '\n');
|
2020-02-15 07:40:47 +01:00
|
|
|
OS.emitIntValue(ConstEntry.second, 8);
|
2014-05-02 00:21:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Emit the callsite info for each callsite.
|
|
|
|
///
|
2013-10-31 23:11:56 +01:00
|
|
|
/// StkMapRecord[NumRecords] {
|
2013-12-13 19:37:10 +01:00
|
|
|
/// uint64 : PatchPoint ID
|
2013-10-31 23:11:56 +01:00
|
|
|
/// uint32 : Instruction Offset
|
|
|
|
/// uint16 : Reserved (record flags)
|
|
|
|
/// uint16 : NumLocations
|
|
|
|
/// Location[NumLocations] {
|
|
|
|
/// uint8 : Register | Direct | Indirect | Constant | ConstantIndex
|
2013-11-17 02:36:23 +01:00
|
|
|
/// uint8 : Size in Bytes
|
2013-10-31 23:11:56 +01:00
|
|
|
/// uint16 : Dwarf RegNum
|
|
|
|
/// int32 : Offset
|
|
|
|
/// }
|
2014-04-01 00:14:04 +02:00
|
|
|
/// uint16 : Padding
|
2013-12-14 07:53:06 +01:00
|
|
|
/// uint16 : NumLiveOuts
|
2014-04-01 00:14:04 +02:00
|
|
|
/// LiveOuts[NumLiveOuts] {
|
2013-12-14 07:53:06 +01:00
|
|
|
/// uint16 : Dwarf RegNum
|
|
|
|
/// uint8 : Reserved
|
|
|
|
/// uint8 : Size in Bytes
|
2014-04-01 00:14:04 +02:00
|
|
|
/// }
|
|
|
|
/// uint32 : Padding (only if required to align to 8 byte)
|
2013-10-31 23:11:56 +01:00
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// Location Encoding, Type, Value:
|
|
|
|
/// 0x1, Register, Reg (value in register)
|
|
|
|
/// 0x2, Direct, Reg + Offset (frame index)
|
|
|
|
/// 0x3, Indirect, [Reg + Offset] (spilled value)
|
|
|
|
/// 0x4, Constant, Offset (small constant)
|
|
|
|
/// 0x5, ConstIndex, Constants[Offset] (large constant)
|
2015-03-20 22:05:18 +01:00
|
|
|
void StackMaps::emitCallsiteEntries(MCStreamer &OS) {
|
2018-05-14 14:53:11 +02:00
|
|
|
LLVM_DEBUG(print(dbgs()));
|
2014-04-01 00:14:04 +02:00
|
|
|
// Callsite entries.
|
2014-05-02 00:21:27 +02:00
|
|
|
for (const auto &CSI : CSInfos) {
|
|
|
|
const LocationVec &CSLocs = CSI.Locations;
|
|
|
|
const LiveOutVec &LiveOuts = CSI.LiveOuts;
|
2013-10-31 23:11:56 +01:00
|
|
|
|
|
|
|
// Verify stack map entry. It's better to communicate a problem to the
|
|
|
|
// runtime than crash in case of in-process compilation. Currently, we do
|
|
|
|
// simple overflow checks, but we may eventually communicate other
|
|
|
|
// compilation errors this way.
|
2013-12-14 07:53:06 +01:00
|
|
|
if (CSLocs.size() > UINT16_MAX || LiveOuts.size() > UINT16_MAX) {
|
2020-02-15 07:40:47 +01:00
|
|
|
OS.emitIntValue(UINT64_MAX, 8); // Invalid ID.
|
|
|
|
OS.emitValue(CSI.CSOffsetExpr, 4);
|
2020-02-29 17:25:22 +01:00
|
|
|
OS.emitInt16(0); // Reserved.
|
|
|
|
OS.emitInt16(0); // 0 locations.
|
|
|
|
OS.emitInt16(0); // padding.
|
|
|
|
OS.emitInt16(0); // 0 live-out registers.
|
|
|
|
OS.emitInt32(0); // padding.
|
2013-10-31 23:11:56 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-02-15 07:40:47 +01:00
|
|
|
OS.emitIntValue(CSI.ID, 8);
|
|
|
|
OS.emitValue(CSI.CSOffsetExpr, 4);
|
2013-10-31 23:11:56 +01:00
|
|
|
|
|
|
|
// Reserved for flags.
|
2020-02-29 17:25:22 +01:00
|
|
|
OS.emitInt16(0);
|
|
|
|
OS.emitInt16(CSLocs.size());
|
2013-10-31 23:11:56 +01:00
|
|
|
|
2014-05-02 00:21:27 +02:00
|
|
|
for (const auto &Loc : CSLocs) {
|
2020-02-15 07:40:47 +01:00
|
|
|
OS.emitIntValue(Loc.Type, 1);
|
|
|
|
OS.emitIntValue(0, 1); // Reserved
|
2020-02-29 17:25:22 +01:00
|
|
|
OS.emitInt16(Loc.Size);
|
|
|
|
OS.emitInt16(Loc.Reg);
|
|
|
|
OS.emitInt16(0); // Reserved
|
|
|
|
OS.emitInt32(Loc.Offset);
|
2013-10-31 23:11:56 +01:00
|
|
|
}
|
2013-12-14 07:53:06 +01:00
|
|
|
|
2017-04-28 06:48:42 +02:00
|
|
|
// Emit alignment to 8 byte.
|
2020-02-15 04:21:58 +01:00
|
|
|
OS.emitValueToAlignment(8);
|
2017-04-28 06:48:42 +02:00
|
|
|
|
2014-04-01 00:14:04 +02:00
|
|
|
// Num live-out registers and padding to align to 4 byte.
|
2020-02-29 17:25:22 +01:00
|
|
|
OS.emitInt16(0);
|
|
|
|
OS.emitInt16(LiveOuts.size());
|
2014-05-02 00:21:27 +02:00
|
|
|
|
|
|
|
for (const auto &LO : LiveOuts) {
|
2020-02-29 17:25:22 +01:00
|
|
|
OS.emitInt16(LO.DwarfRegNum);
|
2020-02-15 07:40:47 +01:00
|
|
|
OS.emitIntValue(0, 1);
|
|
|
|
OS.emitIntValue(LO.Size, 1);
|
2013-12-14 07:53:06 +01:00
|
|
|
}
|
2014-04-01 00:14:04 +02:00
|
|
|
// Emit alignment to 8 byte.
|
2020-02-15 04:21:58 +01:00
|
|
|
OS.emitValueToAlignment(8);
|
2013-10-31 23:11:56 +01:00
|
|
|
}
|
2014-05-02 00:21:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Serialize the stackmap data.
|
|
|
|
void StackMaps::serializeToStackMapSection() {
|
2015-07-09 00:42:09 +02:00
|
|
|
(void)WSMP;
|
2014-05-02 00:21:27 +02:00
|
|
|
// Bail out if there's no stack map data.
|
2016-04-11 22:35:01 +02:00
|
|
|
assert((!CSInfos.empty() || ConstPool.empty()) &&
|
2014-05-02 00:21:27 +02:00
|
|
|
"Expected empty constant pool too!");
|
2016-09-14 22:22:03 +02:00
|
|
|
assert((!CSInfos.empty() || FnInfos.empty()) &&
|
2014-05-02 00:21:27 +02:00
|
|
|
"Expected empty function record too!");
|
|
|
|
if (CSInfos.empty())
|
|
|
|
return;
|
|
|
|
|
2015-04-24 21:11:51 +02:00
|
|
|
MCContext &OutContext = AP.OutStreamer->getContext();
|
|
|
|
MCStreamer &OS = *AP.OutStreamer;
|
2013-10-31 23:11:56 +01:00
|
|
|
|
2014-05-02 00:21:27 +02:00
|
|
|
// Create the section.
|
2015-05-21 21:20:38 +02:00
|
|
|
MCSection *StackMapSection =
|
|
|
|
OutContext.getObjectFileInfo()->getStackMapSection();
|
2014-05-02 00:21:27 +02:00
|
|
|
OS.SwitchSection(StackMapSection);
|
|
|
|
|
|
|
|
// Emit a dummy symbol to force section inclusion.
|
2020-02-15 04:21:58 +01:00
|
|
|
OS.emitLabel(OutContext.getOrCreateSymbol(Twine("__LLVM_StackMaps")));
|
2014-05-02 00:21:27 +02:00
|
|
|
|
|
|
|
// Serialize data.
|
2018-05-14 14:53:11 +02:00
|
|
|
LLVM_DEBUG(dbgs() << "********** Stack Map Output **********\n");
|
2014-05-02 00:21:27 +02:00
|
|
|
emitStackmapHeader(OS);
|
|
|
|
emitFunctionFrameRecords(OS);
|
|
|
|
emitConstantPoolEntries(OS);
|
2015-03-20 22:05:18 +01:00
|
|
|
emitCallsiteEntries(OS);
|
2014-05-02 00:21:27 +02:00
|
|
|
OS.AddBlankLine();
|
2013-10-31 23:11:56 +01:00
|
|
|
|
2014-05-02 00:21:27 +02:00
|
|
|
// Clean up.
|
2013-10-31 23:11:56 +01:00
|
|
|
CSInfos.clear();
|
2014-05-02 00:21:24 +02:00
|
|
|
ConstPool.clear();
|
2013-10-31 23:11:56 +01:00
|
|
|
}
|