2018-03-08 14:05:02 +01:00
|
|
|
//===--------------------- InstrBuilder.cpp ---------------------*- C++ -*-===//
|
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// \file
|
|
|
|
///
|
|
|
|
/// This file implements the InstrBuilder interface.
|
|
|
|
///
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "InstrBuilder.h"
|
2018-06-20 12:08:11 +02:00
|
|
|
#include "llvm/ADT/APInt.h"
|
2018-06-04 14:23:07 +02:00
|
|
|
#include "llvm/ADT/DenseMap.h"
|
2018-03-08 14:05:02 +01:00
|
|
|
#include "llvm/MC/MCInst.h"
|
|
|
|
#include "llvm/Support/Debug.h"
|
2018-05-04 15:52:12 +02:00
|
|
|
#include "llvm/Support/WithColor.h"
|
2018-07-09 14:30:55 +02:00
|
|
|
#include "llvm/Support/raw_ostream.h"
|
2018-03-08 14:05:02 +01:00
|
|
|
|
|
|
|
#define DEBUG_TYPE "llvm-mca"
|
|
|
|
|
|
|
|
namespace mca {
|
|
|
|
|
|
|
|
using namespace llvm;
|
|
|
|
|
2018-03-24 17:05:36 +01:00
|
|
|
static void initializeUsedResources(InstrDesc &ID,
|
|
|
|
const MCSchedClassDesc &SCDesc,
|
|
|
|
const MCSubtargetInfo &STI,
|
|
|
|
ArrayRef<uint64_t> ProcResourceMasks) {
|
2018-03-08 14:05:02 +01:00
|
|
|
const MCSchedModel &SM = STI.getSchedModel();
|
|
|
|
|
|
|
|
// Populate resources consumed.
|
|
|
|
using ResourcePlusCycles = std::pair<uint64_t, ResourceUsage>;
|
|
|
|
std::vector<ResourcePlusCycles> Worklist;
|
2018-06-04 14:23:07 +02:00
|
|
|
|
|
|
|
// Track cycles contributed by resources that are in a "Super" relationship.
|
|
|
|
// This is required if we want to correctly match the behavior of method
|
|
|
|
// SubtargetEmitter::ExpandProcResource() in Tablegen. When computing the set
|
|
|
|
// of "consumed" processor resources and resource cycles, the logic in
|
|
|
|
// ExpandProcResource() doesn't update the number of resource cycles
|
|
|
|
// contributed by a "Super" resource to a group.
|
|
|
|
// We need to take this into account when we find that a processor resource is
|
|
|
|
// part of a group, and it is also used as the "Super" of other resources.
|
|
|
|
// This map stores the number of cycles contributed by sub-resources that are
|
|
|
|
// part of a "Super" resource. The key value is the "Super" resource mask ID.
|
|
|
|
DenseMap<uint64_t, unsigned> SuperResources;
|
|
|
|
|
2018-03-08 14:05:02 +01:00
|
|
|
for (unsigned I = 0, E = SCDesc.NumWriteProcResEntries; I < E; ++I) {
|
|
|
|
const MCWriteProcResEntry *PRE = STI.getWriteProcResBegin(&SCDesc) + I;
|
|
|
|
const MCProcResourceDesc &PR = *SM.getProcResource(PRE->ProcResourceIdx);
|
|
|
|
uint64_t Mask = ProcResourceMasks[PRE->ProcResourceIdx];
|
|
|
|
if (PR.BufferSize != -1)
|
|
|
|
ID.Buffers.push_back(Mask);
|
|
|
|
CycleSegment RCy(0, PRE->Cycles, false);
|
|
|
|
Worklist.emplace_back(ResourcePlusCycles(Mask, ResourceUsage(RCy)));
|
2018-06-04 14:23:07 +02:00
|
|
|
if (PR.SuperIdx) {
|
|
|
|
uint64_t Super = ProcResourceMasks[PR.SuperIdx];
|
|
|
|
SuperResources[Super] += PRE->Cycles;
|
|
|
|
}
|
2018-03-08 14:05:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Sort elements by mask popcount, so that we prioritize resource units over
|
|
|
|
// resource groups, and smaller groups over larger groups.
|
2018-04-01 23:24:53 +02:00
|
|
|
llvm::sort(Worklist.begin(), Worklist.end(),
|
|
|
|
[](const ResourcePlusCycles &A, const ResourcePlusCycles &B) {
|
|
|
|
unsigned popcntA = countPopulation(A.first);
|
|
|
|
unsigned popcntB = countPopulation(B.first);
|
|
|
|
if (popcntA < popcntB)
|
|
|
|
return true;
|
|
|
|
if (popcntA > popcntB)
|
|
|
|
return false;
|
|
|
|
return A.first < B.first;
|
|
|
|
});
|
2018-03-08 14:05:02 +01:00
|
|
|
|
|
|
|
uint64_t UsedResourceUnits = 0;
|
|
|
|
|
|
|
|
// Remove cycles contributed by smaller resources.
|
|
|
|
for (unsigned I = 0, E = Worklist.size(); I < E; ++I) {
|
|
|
|
ResourcePlusCycles &A = Worklist[I];
|
|
|
|
if (!A.second.size()) {
|
|
|
|
A.second.NumUnits = 0;
|
|
|
|
A.second.setReserved();
|
|
|
|
ID.Resources.emplace_back(A);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
ID.Resources.emplace_back(A);
|
|
|
|
uint64_t NormalizedMask = A.first;
|
|
|
|
if (countPopulation(A.first) == 1) {
|
|
|
|
UsedResourceUnits |= A.first;
|
|
|
|
} else {
|
|
|
|
// Remove the leading 1 from the resource group mask.
|
|
|
|
NormalizedMask ^= PowerOf2Floor(NormalizedMask);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (unsigned J = I + 1; J < E; ++J) {
|
|
|
|
ResourcePlusCycles &B = Worklist[J];
|
|
|
|
if ((NormalizedMask & B.first) == NormalizedMask) {
|
2018-06-04 14:23:07 +02:00
|
|
|
B.second.CS.Subtract(A.second.size() - SuperResources[A.first]);
|
2018-03-08 14:05:02 +01:00
|
|
|
if (countPopulation(B.first) > 1)
|
|
|
|
B.second.NumUnits++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// A SchedWrite may specify a number of cycles in which a resource group
|
|
|
|
// is reserved. For example (on target x86; cpu Haswell):
|
|
|
|
//
|
|
|
|
// SchedWriteRes<[HWPort0, HWPort1, HWPort01]> {
|
|
|
|
// let ResourceCycles = [2, 2, 3];
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// This means:
|
|
|
|
// Resource units HWPort0 and HWPort1 are both used for 2cy.
|
|
|
|
// Resource group HWPort01 is the union of HWPort0 and HWPort1.
|
|
|
|
// Since this write touches both HWPort0 and HWPort1 for 2cy, HWPort01
|
|
|
|
// will not be usable for 2 entire cycles from instruction issue.
|
|
|
|
//
|
|
|
|
// On top of those 2cy, SchedWriteRes explicitly specifies an extra latency
|
|
|
|
// of 3 cycles for HWPort01. This tool assumes that the 3cy latency is an
|
|
|
|
// extra delay on top of the 2 cycles latency.
|
|
|
|
// During those extra cycles, HWPort01 is not usable by other instructions.
|
|
|
|
for (ResourcePlusCycles &RPC : ID.Resources) {
|
|
|
|
if (countPopulation(RPC.first) > 1 && !RPC.second.isReserved()) {
|
|
|
|
// Remove the leading 1 from the resource group mask.
|
|
|
|
uint64_t Mask = RPC.first ^ PowerOf2Floor(RPC.first);
|
|
|
|
if ((Mask & UsedResourceUnits) == Mask)
|
|
|
|
RPC.second.setReserved();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-14 14:53:11 +02:00
|
|
|
LLVM_DEBUG({
|
2018-03-08 14:05:02 +01:00
|
|
|
for (const std::pair<uint64_t, ResourceUsage> &R : ID.Resources)
|
|
|
|
dbgs() << "\t\tMask=" << R.first << ", cy=" << R.second.size() << '\n';
|
|
|
|
for (const uint64_t R : ID.Buffers)
|
|
|
|
dbgs() << "\t\tBuffer Mask=" << R << '\n';
|
2018-03-20 13:58:34 +01:00
|
|
|
});
|
2018-03-08 14:05:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void computeMaxLatency(InstrDesc &ID, const MCInstrDesc &MCDesc,
|
|
|
|
const MCSchedClassDesc &SCDesc,
|
|
|
|
const MCSubtargetInfo &STI) {
|
|
|
|
if (MCDesc.isCall()) {
|
|
|
|
// We cannot estimate how long this call will take.
|
|
|
|
// Artificially set an arbitrarily high latency (100cy).
|
2018-03-13 16:59:59 +01:00
|
|
|
ID.MaxLatency = 100U;
|
|
|
|
return;
|
2018-03-08 14:05:02 +01:00
|
|
|
}
|
|
|
|
|
2018-03-13 16:59:59 +01:00
|
|
|
int Latency = MCSchedModel::computeInstrLatency(STI, SCDesc);
|
|
|
|
// If latency is unknown, then conservatively assume a MaxLatency of 100cy.
|
|
|
|
ID.MaxLatency = Latency < 0 ? 100U : static_cast<unsigned>(Latency);
|
2018-03-08 14:05:02 +01:00
|
|
|
}
|
|
|
|
|
2018-08-13 20:11:48 +02:00
|
|
|
Error InstrBuilder::populateWrites(InstrDesc &ID, const MCInst &MCI,
|
|
|
|
unsigned SchedClassID) {
|
2018-07-09 14:30:55 +02:00
|
|
|
const MCInstrDesc &MCDesc = MCII.get(MCI.getOpcode());
|
|
|
|
const MCSchedModel &SM = STI.getSchedModel();
|
|
|
|
const MCSchedClassDesc &SCDesc = *SM.getSchedClassDesc(SchedClassID);
|
|
|
|
|
2018-06-21 14:14:49 +02:00
|
|
|
// These are for now the (strong) assumptions made by this algorithm:
|
2018-03-08 14:05:02 +01:00
|
|
|
// * The number of explicit and implicit register definitions in a MCInst
|
|
|
|
// matches the number of explicit and implicit definitions according to
|
|
|
|
// the opcode descriptor (MCInstrDesc).
|
|
|
|
// * Register definitions take precedence over register uses in the operands
|
|
|
|
// list.
|
|
|
|
// * If an opcode specifies an optional definition, then the optional
|
|
|
|
// definition is always the last operand in the sequence, and it can be
|
|
|
|
// set to zero (i.e. "no register").
|
|
|
|
//
|
|
|
|
// These assumptions work quite well for most out-of-order in-tree targets
|
|
|
|
// like x86. This is mainly because the vast majority of instructions is
|
|
|
|
// expanded to MCInst using a straightforward lowering logic that preserves
|
|
|
|
// the ordering of the operands.
|
|
|
|
unsigned NumExplicitDefs = MCDesc.getNumDefs();
|
|
|
|
unsigned NumImplicitDefs = MCDesc.getNumImplicitDefs();
|
|
|
|
unsigned NumWriteLatencyEntries = SCDesc.NumWriteLatencyEntries;
|
|
|
|
unsigned TotalDefs = NumExplicitDefs + NumImplicitDefs;
|
|
|
|
if (MCDesc.hasOptionalDef())
|
|
|
|
TotalDefs++;
|
|
|
|
ID.Writes.resize(TotalDefs);
|
|
|
|
// Iterate over the operands list, and skip non-register operands.
|
|
|
|
// The first NumExplictDefs register operands are expected to be register
|
|
|
|
// definitions.
|
|
|
|
unsigned CurrentDef = 0;
|
|
|
|
unsigned i = 0;
|
|
|
|
for (; i < MCI.getNumOperands() && CurrentDef < NumExplicitDefs; ++i) {
|
|
|
|
const MCOperand &Op = MCI.getOperand(i);
|
|
|
|
if (!Op.isReg())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
WriteDescriptor &Write = ID.Writes[CurrentDef];
|
|
|
|
Write.OpIndex = i;
|
|
|
|
if (CurrentDef < NumWriteLatencyEntries) {
|
|
|
|
const MCWriteLatencyEntry &WLE =
|
|
|
|
*STI.getWriteLatencyEntry(&SCDesc, CurrentDef);
|
|
|
|
// Conservatively default to MaxLatency.
|
2018-07-09 14:30:55 +02:00
|
|
|
Write.Latency =
|
|
|
|
WLE.Cycles < 0 ? ID.MaxLatency : static_cast<unsigned>(WLE.Cycles);
|
2018-03-08 14:05:02 +01:00
|
|
|
Write.SClassOrWriteResourceID = WLE.WriteResourceID;
|
|
|
|
} else {
|
|
|
|
// Assign a default latency for this write.
|
|
|
|
Write.Latency = ID.MaxLatency;
|
|
|
|
Write.SClassOrWriteResourceID = 0;
|
|
|
|
}
|
|
|
|
Write.IsOptionalDef = false;
|
2018-05-14 14:53:11 +02:00
|
|
|
LLVM_DEBUG({
|
2018-07-13 16:55:47 +02:00
|
|
|
dbgs() << "\t\t[Def] OpIdx=" << Write.OpIndex
|
|
|
|
<< ", Latency=" << Write.Latency
|
2018-03-20 13:58:34 +01:00
|
|
|
<< ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n';
|
|
|
|
});
|
2018-03-08 14:05:02 +01:00
|
|
|
CurrentDef++;
|
|
|
|
}
|
|
|
|
|
2018-08-13 20:11:48 +02:00
|
|
|
if (CurrentDef != NumExplicitDefs) {
|
|
|
|
return make_error<StringError>(
|
|
|
|
"error: Expected more register operand definitions.",
|
|
|
|
inconvertibleErrorCode());
|
|
|
|
}
|
2018-03-08 14:05:02 +01:00
|
|
|
|
|
|
|
CurrentDef = 0;
|
|
|
|
for (CurrentDef = 0; CurrentDef < NumImplicitDefs; ++CurrentDef) {
|
|
|
|
unsigned Index = NumExplicitDefs + CurrentDef;
|
|
|
|
WriteDescriptor &Write = ID.Writes[Index];
|
2018-06-22 18:37:05 +02:00
|
|
|
Write.OpIndex = ~CurrentDef;
|
2018-03-08 14:05:02 +01:00
|
|
|
Write.RegisterID = MCDesc.getImplicitDefs()[CurrentDef];
|
2018-04-02 15:46:49 +02:00
|
|
|
if (Index < NumWriteLatencyEntries) {
|
|
|
|
const MCWriteLatencyEntry &WLE =
|
|
|
|
*STI.getWriteLatencyEntry(&SCDesc, Index);
|
|
|
|
// Conservatively default to MaxLatency.
|
2018-07-09 14:30:55 +02:00
|
|
|
Write.Latency =
|
|
|
|
WLE.Cycles < 0 ? ID.MaxLatency : static_cast<unsigned>(WLE.Cycles);
|
2018-04-02 15:46:49 +02:00
|
|
|
Write.SClassOrWriteResourceID = WLE.WriteResourceID;
|
|
|
|
} else {
|
|
|
|
// Assign a default latency for this write.
|
|
|
|
Write.Latency = ID.MaxLatency;
|
|
|
|
Write.SClassOrWriteResourceID = 0;
|
|
|
|
}
|
|
|
|
|
2018-03-08 14:05:02 +01:00
|
|
|
Write.IsOptionalDef = false;
|
|
|
|
assert(Write.RegisterID != 0 && "Expected a valid phys register!");
|
2018-07-13 16:55:47 +02:00
|
|
|
LLVM_DEBUG({
|
|
|
|
dbgs() << "\t\t[Def] OpIdx=" << Write.OpIndex
|
|
|
|
<< ", PhysReg=" << MRI.getName(Write.RegisterID)
|
|
|
|
<< ", Latency=" << Write.Latency
|
|
|
|
<< ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n';
|
|
|
|
});
|
2018-03-08 14:05:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (MCDesc.hasOptionalDef()) {
|
|
|
|
// Always assume that the optional definition is the last operand of the
|
|
|
|
// MCInst sequence.
|
|
|
|
const MCOperand &Op = MCI.getOperand(MCI.getNumOperands() - 1);
|
|
|
|
if (i == MCI.getNumOperands() || !Op.isReg())
|
2018-08-13 20:11:48 +02:00
|
|
|
return make_error<StringError>(
|
2018-03-08 14:05:02 +01:00
|
|
|
"error: expected a register operand for an optional "
|
2018-08-13 20:11:48 +02:00
|
|
|
"definition. Instruction has not be correctly analyzed.",
|
|
|
|
inconvertibleErrorCode());
|
2018-03-08 14:05:02 +01:00
|
|
|
|
|
|
|
WriteDescriptor &Write = ID.Writes[TotalDefs - 1];
|
|
|
|
Write.OpIndex = MCI.getNumOperands() - 1;
|
|
|
|
// Assign a default latency for this write.
|
|
|
|
Write.Latency = ID.MaxLatency;
|
|
|
|
Write.SClassOrWriteResourceID = 0;
|
|
|
|
Write.IsOptionalDef = true;
|
|
|
|
}
|
2018-08-13 20:11:48 +02:00
|
|
|
|
|
|
|
return ErrorSuccess();
|
2018-03-08 14:05:02 +01:00
|
|
|
}
|
|
|
|
|
2018-08-13 20:11:48 +02:00
|
|
|
Error InstrBuilder::populateReads(InstrDesc &ID, const MCInst &MCI,
|
|
|
|
unsigned SchedClassID) {
|
2018-07-09 14:30:55 +02:00
|
|
|
const MCInstrDesc &MCDesc = MCII.get(MCI.getOpcode());
|
2018-03-08 14:05:02 +01:00
|
|
|
unsigned NumExplicitDefs = MCDesc.getNumDefs();
|
2018-07-09 14:30:55 +02:00
|
|
|
|
2018-03-08 14:05:02 +01:00
|
|
|
// Skip explicit definitions.
|
2018-07-09 14:30:55 +02:00
|
|
|
unsigned i = 0;
|
2018-03-08 14:05:02 +01:00
|
|
|
for (; i < MCI.getNumOperands() && NumExplicitDefs; ++i) {
|
|
|
|
const MCOperand &Op = MCI.getOperand(i);
|
|
|
|
if (Op.isReg())
|
|
|
|
NumExplicitDefs--;
|
|
|
|
}
|
|
|
|
|
2018-08-13 20:11:48 +02:00
|
|
|
if (NumExplicitDefs) {
|
|
|
|
return make_error<StringError>(
|
|
|
|
"error: Expected more register operand definitions. ",
|
|
|
|
inconvertibleErrorCode());
|
|
|
|
}
|
2018-03-08 14:05:02 +01:00
|
|
|
|
|
|
|
unsigned NumExplicitUses = MCI.getNumOperands() - i;
|
|
|
|
unsigned NumImplicitUses = MCDesc.getNumImplicitUses();
|
|
|
|
if (MCDesc.hasOptionalDef()) {
|
|
|
|
assert(NumExplicitUses);
|
|
|
|
NumExplicitUses--;
|
|
|
|
}
|
|
|
|
unsigned TotalUses = NumExplicitUses + NumImplicitUses;
|
|
|
|
if (!TotalUses)
|
2018-08-13 20:11:48 +02:00
|
|
|
return ErrorSuccess();
|
2018-03-08 14:05:02 +01:00
|
|
|
|
|
|
|
ID.Reads.resize(TotalUses);
|
|
|
|
for (unsigned CurrentUse = 0; CurrentUse < NumExplicitUses; ++CurrentUse) {
|
|
|
|
ReadDescriptor &Read = ID.Reads[CurrentUse];
|
|
|
|
Read.OpIndex = i + CurrentUse;
|
2018-03-29 16:26:56 +02:00
|
|
|
Read.UseIndex = CurrentUse;
|
2018-03-08 14:05:02 +01:00
|
|
|
Read.SchedClassID = SchedClassID;
|
2018-07-13 16:55:47 +02:00
|
|
|
LLVM_DEBUG(dbgs() << "\t\t[Use] OpIdx=" << Read.OpIndex
|
|
|
|
<< ", UseIndex=" << Read.UseIndex << '\n');
|
2018-03-08 14:05:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for (unsigned CurrentUse = 0; CurrentUse < NumImplicitUses; ++CurrentUse) {
|
|
|
|
ReadDescriptor &Read = ID.Reads[NumExplicitUses + CurrentUse];
|
2018-06-22 18:37:05 +02:00
|
|
|
Read.OpIndex = ~CurrentUse;
|
2018-04-02 15:46:49 +02:00
|
|
|
Read.UseIndex = NumExplicitUses + CurrentUse;
|
2018-03-08 14:05:02 +01:00
|
|
|
Read.RegisterID = MCDesc.getImplicitUses()[CurrentUse];
|
|
|
|
Read.SchedClassID = SchedClassID;
|
2018-07-13 16:55:47 +02:00
|
|
|
LLVM_DEBUG(dbgs() << "\t\t[Use] OpIdx=" << Read.OpIndex << ", RegisterID="
|
|
|
|
<< MRI.getName(Read.RegisterID) << '\n');
|
2018-03-08 14:05:02 +01:00
|
|
|
}
|
2018-08-13 20:11:48 +02:00
|
|
|
return ErrorSuccess();
|
2018-03-08 14:05:02 +01:00
|
|
|
}
|
|
|
|
|
2018-08-13 20:11:48 +02:00
|
|
|
Expected<const InstrDesc &>
|
|
|
|
InstrBuilder::createInstrDescImpl(const MCInst &MCI) {
|
2018-03-08 14:05:02 +01:00
|
|
|
assert(STI.getSchedModel().hasInstrSchedModel() &&
|
|
|
|
"Itineraries are not yet supported!");
|
|
|
|
|
|
|
|
// Obtain the instruction descriptor from the opcode.
|
2018-07-09 14:30:55 +02:00
|
|
|
unsigned short Opcode = MCI.getOpcode();
|
2018-03-08 14:05:02 +01:00
|
|
|
const MCInstrDesc &MCDesc = MCII.get(Opcode);
|
|
|
|
const MCSchedModel &SM = STI.getSchedModel();
|
|
|
|
|
|
|
|
// Then obtain the scheduling class information from the instruction.
|
2018-05-04 15:10:10 +02:00
|
|
|
unsigned SchedClassID = MCDesc.getSchedClass();
|
2018-06-04 17:43:09 +02:00
|
|
|
unsigned CPUID = SM.getProcessorID();
|
|
|
|
|
|
|
|
// Try to solve variant scheduling classes.
|
|
|
|
if (SchedClassID) {
|
|
|
|
while (SchedClassID && SM.getSchedClassDesc(SchedClassID)->isVariant())
|
|
|
|
SchedClassID = STI.resolveVariantSchedClass(SchedClassID, &MCI, CPUID);
|
|
|
|
|
2018-08-13 20:11:48 +02:00
|
|
|
if (!SchedClassID) {
|
|
|
|
return make_error<StringError>("unable to resolve this variant class.",
|
|
|
|
inconvertibleErrorCode());
|
|
|
|
}
|
2018-06-04 17:43:09 +02:00
|
|
|
}
|
2018-03-08 14:05:02 +01:00
|
|
|
|
2018-08-13 20:11:48 +02:00
|
|
|
// Check if this instruction is supported. Otherwise, report an error.
|
2018-07-09 14:30:55 +02:00
|
|
|
const MCSchedClassDesc &SCDesc = *SM.getSchedClassDesc(SchedClassID);
|
|
|
|
if (SCDesc.NumMicroOps == MCSchedClassDesc::InvalidNumMicroOps) {
|
|
|
|
std::string ToString;
|
|
|
|
llvm::raw_string_ostream OS(ToString);
|
|
|
|
WithColor::error() << "found an unsupported instruction in the input"
|
|
|
|
<< " assembly sequence.\n";
|
|
|
|
MCIP.printInst(&MCI, OS, "", STI);
|
|
|
|
OS.flush();
|
|
|
|
WithColor::note() << "instruction: " << ToString << '\n';
|
2018-08-13 20:11:48 +02:00
|
|
|
return make_error<StringError>(
|
|
|
|
"Don't know how to analyze unsupported instructions",
|
|
|
|
inconvertibleErrorCode());
|
2018-07-09 14:30:55 +02:00
|
|
|
}
|
|
|
|
|
2018-03-08 14:05:02 +01:00
|
|
|
// Create a new empty descriptor.
|
2018-03-20 13:58:34 +01:00
|
|
|
std::unique_ptr<InstrDesc> ID = llvm::make_unique<InstrDesc>();
|
2018-06-04 17:43:09 +02:00
|
|
|
ID->NumMicroOps = SCDesc.NumMicroOps;
|
2018-03-08 14:05:02 +01:00
|
|
|
|
|
|
|
if (MCDesc.isCall()) {
|
|
|
|
// We don't correctly model calls.
|
2018-05-04 15:52:12 +02:00
|
|
|
WithColor::warning() << "found a call in the input assembly sequence.\n";
|
|
|
|
WithColor::note() << "call instructions are not correctly modeled. "
|
|
|
|
<< "Assume a latency of 100cy.\n";
|
2018-03-08 14:05:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (MCDesc.isReturn()) {
|
2018-05-04 15:52:12 +02:00
|
|
|
WithColor::warning() << "found a return instruction in the input"
|
|
|
|
<< " assembly sequence.\n";
|
|
|
|
WithColor::note() << "program counter updates are ignored.\n";
|
2018-03-08 14:05:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ID->MayLoad = MCDesc.mayLoad();
|
|
|
|
ID->MayStore = MCDesc.mayStore();
|
|
|
|
ID->HasSideEffects = MCDesc.hasUnmodeledSideEffects();
|
|
|
|
|
|
|
|
initializeUsedResources(*ID, SCDesc, STI, ProcResourceMasks);
|
2018-04-25 11:38:58 +02:00
|
|
|
computeMaxLatency(*ID, MCDesc, SCDesc, STI);
|
2018-08-13 20:11:48 +02:00
|
|
|
if (auto Err = populateWrites(*ID, MCI, SchedClassID))
|
|
|
|
return std::move(Err);
|
|
|
|
if (auto Err = populateReads(*ID, MCI, SchedClassID))
|
|
|
|
return std::move(Err);
|
2018-03-08 14:05:02 +01:00
|
|
|
|
2018-05-14 14:53:11 +02:00
|
|
|
LLVM_DEBUG(dbgs() << "\t\tMaxLatency=" << ID->MaxLatency << '\n');
|
|
|
|
LLVM_DEBUG(dbgs() << "\t\tNumMicroOps=" << ID->NumMicroOps << '\n');
|
2018-03-08 14:05:02 +01:00
|
|
|
|
|
|
|
// Now add the new descriptor.
|
2018-06-04 17:43:09 +02:00
|
|
|
SchedClassID = MCDesc.getSchedClass();
|
|
|
|
if (!SM.getSchedClassDesc(SchedClassID)->isVariant()) {
|
|
|
|
Descriptors[MCI.getOpcode()] = std::move(ID);
|
|
|
|
return *Descriptors[MCI.getOpcode()];
|
|
|
|
}
|
|
|
|
|
|
|
|
VariantDescriptors[&MCI] = std::move(ID);
|
|
|
|
return *VariantDescriptors[&MCI];
|
2018-03-08 14:05:02 +01:00
|
|
|
}
|
|
|
|
|
2018-08-13 20:11:48 +02:00
|
|
|
Expected<const InstrDesc &>
|
|
|
|
InstrBuilder::getOrCreateInstrDesc(const MCInst &MCI) {
|
2018-06-04 17:43:09 +02:00
|
|
|
if (Descriptors.find_as(MCI.getOpcode()) != Descriptors.end())
|
|
|
|
return *Descriptors[MCI.getOpcode()];
|
|
|
|
|
|
|
|
if (VariantDescriptors.find(&MCI) != VariantDescriptors.end())
|
|
|
|
return *VariantDescriptors[&MCI];
|
|
|
|
|
|
|
|
return createInstrDescImpl(MCI);
|
2018-03-08 14:05:02 +01:00
|
|
|
}
|
|
|
|
|
2018-08-13 20:11:48 +02:00
|
|
|
Expected<std::unique_ptr<Instruction>>
|
2018-05-04 15:10:10 +02:00
|
|
|
InstrBuilder::createInstruction(const MCInst &MCI) {
|
2018-08-13 20:11:48 +02:00
|
|
|
Expected<const InstrDesc &> DescOrErr = getOrCreateInstrDesc(MCI);
|
|
|
|
if (!DescOrErr)
|
|
|
|
return DescOrErr.takeError();
|
|
|
|
const InstrDesc &D = *DescOrErr;
|
2018-03-20 13:58:34 +01:00
|
|
|
std::unique_ptr<Instruction> NewIS = llvm::make_unique<Instruction>(D);
|
2018-03-08 14:05:02 +01:00
|
|
|
|
2018-09-18 17:00:06 +02:00
|
|
|
// Check if this is a dependency breaking instruction.
|
|
|
|
bool IsDepBreaking = MCIA.isDependencyBreaking(STI, MCI);
|
|
|
|
// FIXME: this is a temporary hack to identify zero-idioms.
|
|
|
|
bool IsZeroIdiom = D.isZeroLatency() && IsDepBreaking;
|
|
|
|
|
2018-04-25 11:38:58 +02:00
|
|
|
// Initialize Reads first.
|
2018-03-08 14:05:02 +01:00
|
|
|
for (const ReadDescriptor &RD : D.Reads) {
|
|
|
|
int RegID = -1;
|
2018-06-22 18:37:05 +02:00
|
|
|
if (!RD.isImplicitRead()) {
|
2018-03-08 14:05:02 +01:00
|
|
|
// explicit read.
|
|
|
|
const MCOperand &Op = MCI.getOperand(RD.OpIndex);
|
|
|
|
// Skip non-register operands.
|
|
|
|
if (!Op.isReg())
|
|
|
|
continue;
|
|
|
|
RegID = Op.getReg();
|
|
|
|
} else {
|
|
|
|
// Implicit read.
|
|
|
|
RegID = RD.RegisterID;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Skip invalid register operands.
|
|
|
|
if (!RegID)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Okay, this is a register operand. Create a ReadState for it.
|
|
|
|
assert(RegID > 0 && "Invalid register ID found!");
|
2018-09-18 17:00:06 +02:00
|
|
|
auto RS = llvm::make_unique<ReadState>(RD, RegID);
|
|
|
|
|
|
|
|
if (IsDepBreaking && !RD.isImplicitRead())
|
|
|
|
RS->setIndependentFromDef();
|
|
|
|
NewIS->getUses().emplace_back(std::move(RS));
|
2018-03-20 13:25:54 +01:00
|
|
|
}
|
2018-03-08 14:05:02 +01:00
|
|
|
|
2018-06-20 12:08:11 +02:00
|
|
|
// Early exit if there are no writes.
|
|
|
|
if (D.Writes.empty())
|
2018-08-13 20:11:48 +02:00
|
|
|
return std::move(NewIS);
|
2018-06-20 12:08:11 +02:00
|
|
|
|
|
|
|
// Track register writes that implicitly clear the upper portion of the
|
|
|
|
// underlying super-registers using an APInt.
|
|
|
|
APInt WriteMask(D.Writes.size(), 0);
|
|
|
|
|
|
|
|
// Now query the MCInstrAnalysis object to obtain information about which
|
|
|
|
// register writes implicitly clear the upper portion of a super-register.
|
|
|
|
MCIA.clearsSuperRegisters(MRI, MCI, WriteMask);
|
|
|
|
|
2018-04-25 11:38:58 +02:00
|
|
|
// Initialize writes.
|
2018-06-20 12:08:11 +02:00
|
|
|
unsigned WriteIndex = 0;
|
2018-03-08 14:05:02 +01:00
|
|
|
for (const WriteDescriptor &WD : D.Writes) {
|
2018-07-09 14:30:55 +02:00
|
|
|
unsigned RegID = WD.isImplicitWrite() ? WD.RegisterID
|
|
|
|
: MCI.getOperand(WD.OpIndex).getReg();
|
2018-03-22 11:19:20 +01:00
|
|
|
// Check if this is a optional definition that references NoReg.
|
2018-06-20 12:08:11 +02:00
|
|
|
if (WD.IsOptionalDef && !RegID) {
|
|
|
|
++WriteIndex;
|
2018-03-08 14:05:02 +01:00
|
|
|
continue;
|
2018-06-20 12:08:11 +02:00
|
|
|
}
|
2018-03-08 14:05:02 +01:00
|
|
|
|
2018-03-22 11:19:20 +01:00
|
|
|
assert(RegID && "Expected a valid register ID!");
|
2018-06-20 16:30:17 +02:00
|
|
|
NewIS->getDefs().emplace_back(llvm::make_unique<WriteState>(
|
2018-09-18 17:00:06 +02:00
|
|
|
WD, RegID, /* ClearsSuperRegs */ WriteMask[WriteIndex],
|
|
|
|
/* WritesZero */ IsZeroIdiom));
|
2018-06-20 12:08:11 +02:00
|
|
|
++WriteIndex;
|
2018-03-08 14:05:02 +01:00
|
|
|
}
|
|
|
|
|
2018-08-13 20:11:48 +02:00
|
|
|
return std::move(NewIS);
|
2018-03-08 14:05:02 +01:00
|
|
|
}
|
|
|
|
} // namespace mca
|