forked from OSchip/llvm-project
GlobalISel: implement low-level type with just size & vector lanes.
This should be all the low-level instruction selection needs to determine how to implement an operation, with the remaining context taken from the opcode (e.g. G_ADD vs G_FADD) or other flags not based on type (e.g. fast-math). llvm-svn: 276158
This commit is contained in:
parent
228d27c70f
commit
62ae568bbb
|
@ -17,6 +17,7 @@
|
|||
#include "llvm/CodeGen/GlobalISel/Types.h"
|
||||
|
||||
#include "llvm/CodeGen/MachineBasicBlock.h"
|
||||
#include "llvm/CodeGen/LowLevelType.h"
|
||||
#include "llvm/IR/DebugLoc.h"
|
||||
|
||||
namespace llvm {
|
||||
|
@ -96,7 +97,7 @@ public:
|
|||
/// \pre Ty == nullptr or isPreISelGenericOpcode(Opcode)
|
||||
///
|
||||
/// \return The newly created instruction.
|
||||
MachineInstr *buildInstr(unsigned Opcode, Type *Ty);
|
||||
MachineInstr *buildInstr(unsigned Opcode, LLT Ty);
|
||||
|
||||
/// Build and insert <empty> = \p Opcode [\p Ty] \p BB.
|
||||
///
|
||||
|
@ -104,7 +105,7 @@ public:
|
|||
/// \pre Ty == nullptr or isPreISelGenericOpcode(Opcode)
|
||||
///
|
||||
/// \return The newly created instruction.
|
||||
MachineInstr *buildInstr(unsigned Opcode, Type *Ty, MachineBasicBlock &BB);
|
||||
MachineInstr *buildInstr(unsigned Opcode, LLT Ty, MachineBasicBlock &BB);
|
||||
|
||||
/// Build and insert \p Res<def> = \p Opcode [\p Ty] \p Op0, \p Op1.
|
||||
///
|
||||
|
@ -112,7 +113,7 @@ public:
|
|||
/// \pre Ty == nullptr or isPreISelGenericOpcode(Opcode)
|
||||
///
|
||||
/// \return The newly created instruction.
|
||||
MachineInstr *buildInstr(unsigned Opcode, Type *Ty, unsigned Res,
|
||||
MachineInstr *buildInstr(unsigned Opcode, LLT Ty, unsigned Res,
|
||||
unsigned Op0, unsigned Op1);
|
||||
|
||||
/// Build and insert \p Res<def> = \p Opcode \p Op0, \p Op1.
|
||||
|
|
|
@ -291,9 +291,6 @@ protected:
|
|||
/// Total number of register banks.
|
||||
unsigned NumRegBanks;
|
||||
|
||||
/// Mapping from MVT::SimpleValueType to register banks.
|
||||
std::unique_ptr<const RegisterBank *[]> VTToRegBank;
|
||||
|
||||
/// Create a RegisterBankInfo that can accomodate up to \p NumRegBanks
|
||||
/// RegisterBank instances.
|
||||
///
|
||||
|
@ -325,14 +322,6 @@ protected:
|
|||
/// It also adjusts the size of the register bank to reflect the maximal
|
||||
/// size of a value that can be hold into that register bank.
|
||||
///
|
||||
/// If \p AddTypeMapping is true, this method also records what types can
|
||||
/// be mapped to \p ID. Although this done by default, targets may want to
|
||||
/// disable it, espicially if a given type may be mapped on different
|
||||
/// register bank. Indeed, in such case, this method only records the
|
||||
/// first register bank where the type matches.
|
||||
/// This information is only used to provide default mapping
|
||||
/// (see getInstrMappingImpl).
|
||||
///
|
||||
/// \note This method does *not* add the super classes of \p RCId.
|
||||
/// The rationale is if \p ID covers the registers of \p RCId, that
|
||||
/// does not necessarily mean that \p ID covers the set of registers
|
||||
|
@ -343,8 +332,7 @@ protected:
|
|||
///
|
||||
/// \todo TableGen should just generate the BitSet vector for us.
|
||||
void addRegBankCoverage(unsigned ID, unsigned RCId,
|
||||
const TargetRegisterInfo &TRI,
|
||||
bool AddTypeMapping = true);
|
||||
const TargetRegisterInfo &TRI);
|
||||
|
||||
/// Get the register bank identified by \p ID.
|
||||
RegisterBank &getRegBank(unsigned ID) {
|
||||
|
@ -352,36 +340,6 @@ protected:
|
|||
return RegBanks[ID];
|
||||
}
|
||||
|
||||
/// Get the register bank that has been recorded to cover \p SVT.
|
||||
const RegisterBank *getRegBankForType(MVT::SimpleValueType SVT) const {
|
||||
if (!VTToRegBank)
|
||||
return nullptr;
|
||||
assert(SVT < MVT::SimpleValueType::LAST_VALUETYPE && "Out-of-bound access");
|
||||
return VTToRegBank.get()[SVT];
|
||||
}
|
||||
|
||||
/// Record \p RegBank as the register bank that covers \p SVT.
|
||||
/// If a record was already set for \p SVT, the mapping is not
|
||||
/// updated, unless \p Force == true
|
||||
///
|
||||
/// \post if getRegBankForType(SVT)\@pre == nullptr then
|
||||
/// getRegBankForType(SVT) == &RegBank
|
||||
/// \post if Force == true then getRegBankForType(SVT) == &RegBank
|
||||
void recordRegBankForType(const RegisterBank &RegBank,
|
||||
MVT::SimpleValueType SVT, bool Force = false) {
|
||||
if (!VTToRegBank) {
|
||||
VTToRegBank.reset(
|
||||
new const RegisterBank *[MVT::SimpleValueType::LAST_VALUETYPE]);
|
||||
std::fill(&VTToRegBank[0],
|
||||
&VTToRegBank[MVT::SimpleValueType::LAST_VALUETYPE], nullptr);
|
||||
}
|
||||
assert(SVT < MVT::SimpleValueType::LAST_VALUETYPE && "Out-of-bound access");
|
||||
// If we want to override the mapping or the mapping does not exits yet,
|
||||
// set the register bank for SVT.
|
||||
if (Force || !getRegBankForType(SVT))
|
||||
VTToRegBank.get()[SVT] = &RegBank;
|
||||
}
|
||||
|
||||
/// Try to get the mapping of \p MI.
|
||||
/// See getInstrMapping for more details on what a mapping represents.
|
||||
///
|
||||
|
|
|
@ -0,0 +1,187 @@
|
|||
//== llvm/CodeGen/GlobalISel/LowLevelType.h -------------------- -*- C++ -*-==//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
/// Implement a low-level type suitable for MachineInstr level instruction
|
||||
/// selection.
|
||||
///
|
||||
/// For a type attached to a MachineInstr, we only care about 2 details: total
|
||||
/// size and the number of vector lanes (if any). Accordingly, there are 3
|
||||
/// possible valid type-kinds:
|
||||
///
|
||||
/// * `unsized` for labels etc
|
||||
/// * `sN` for scalars and aggregates
|
||||
/// * `<N x sM>` for vectors, which must have at least 2 elements.
|
||||
///
|
||||
/// Other information required for correct selection is expected to be carried
|
||||
/// by the opcode, or non-type flags. For example the distinction between G_ADD
|
||||
/// and G_FADD for int/float or fast-math flags.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CODEGEN_GLOBALISEL_LOWLEVELTYPE_H
|
||||
#define LLVM_CODEGEN_GLOBALISEL_LOWLEVELTYPE_H
|
||||
|
||||
#include <cassert>
|
||||
#include "llvm/ADT/DenseMapInfo.h"
|
||||
#include "llvm/CodeGen/ValueTypes.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class LLVMContext;
|
||||
class Type;
|
||||
class raw_ostream;
|
||||
|
||||
class LLT {
|
||||
public:
|
||||
enum TypeKind : uint16_t {
|
||||
Invalid,
|
||||
Scalar,
|
||||
Vector,
|
||||
Unsized,
|
||||
};
|
||||
|
||||
/// \brief get a low-level scalar or aggregate "bag of bits".
|
||||
static LLT scalar(unsigned SizeInBits) {
|
||||
return LLT{Scalar, 1, SizeInBits};
|
||||
}
|
||||
|
||||
/// \brief get a low-level vector of some number of elements and element
|
||||
/// width. \p NumElements must be at least 2.
|
||||
static LLT vector(uint16_t NumElements, unsigned ScalarSizeInBits) {
|
||||
assert(NumElements > 1 && "invalid number of vector elements");
|
||||
return LLT{Vector, NumElements, ScalarSizeInBits};
|
||||
}
|
||||
|
||||
/// \brief get a low-level vector of some number of elements and element
|
||||
/// type
|
||||
static LLT vector(uint16_t NumElements, LLT ScalarTy) {
|
||||
assert(NumElements > 1 && "invalid number of vector elements");
|
||||
assert(ScalarTy.isScalar() && "invalid vector element type");
|
||||
return LLT{Vector, NumElements, ScalarTy.getSizeInBits()};
|
||||
}
|
||||
|
||||
/// \brief get an unsized but valid low-level type (e.g. for a label).
|
||||
static LLT unsized() {
|
||||
return LLT{Unsized, 1, 0};
|
||||
}
|
||||
|
||||
explicit LLT(TypeKind Kind, uint16_t NumElements, unsigned ScalarSizeInBits)
|
||||
: ScalarSize(ScalarSizeInBits), NumElements(NumElements), Kind(Kind) {
|
||||
assert((Kind != Vector || NumElements > 1) &&
|
||||
"invalid number of vector elements");
|
||||
}
|
||||
|
||||
explicit LLT() : ScalarSize(0), NumElements(0), Kind(Invalid) {}
|
||||
|
||||
/// \brief construct a low-level type based on an LLVM type.
|
||||
explicit LLT(const Type &Ty);
|
||||
|
||||
bool isValid() const { return Kind != Invalid; }
|
||||
|
||||
bool isScalar() const { return Kind == Scalar; }
|
||||
|
||||
bool isVector() const { return Kind == Vector; }
|
||||
|
||||
bool isSized() const { return Kind == Scalar || Kind == Vector; }
|
||||
|
||||
/// \brief Returns the number of elements in a vector LLT. Must only be called
|
||||
/// on vector types.
|
||||
uint16_t getNumElements() const {
|
||||
assert(isVector() && "cannot get number of elements on scalar/aggregate");
|
||||
return NumElements;
|
||||
}
|
||||
|
||||
/// \brief Returns the total size of the type. Must only be called on sized
|
||||
/// types.
|
||||
unsigned getSizeInBits() const {
|
||||
assert(isSized() && "attempt to get size of unsized type");
|
||||
return ScalarSize * NumElements;
|
||||
}
|
||||
|
||||
unsigned getScalarSizeInBits() const {
|
||||
assert(isSized() && "cannot get size of this type");
|
||||
return ScalarSize;
|
||||
}
|
||||
|
||||
/// \brief Returns the vector's element type. Only valid for vector types.
|
||||
LLT getElementType() const {
|
||||
assert(isVector() && "cannot get element type of scalar/aggregate");
|
||||
return scalar(ScalarSize);
|
||||
}
|
||||
|
||||
/// \brief get a low-level type with half the size of the original, by halving
|
||||
/// the size of the scalar type involved. For example `s32` will become
|
||||
/// `s16`, `<2 x s32>` will become `<2 x s16>`.
|
||||
LLT halfScalarSize() const {
|
||||
assert(isSized() && "cannot change size of this type");
|
||||
return LLT{Kind, NumElements, ScalarSize / 2};
|
||||
}
|
||||
|
||||
/// \brief get a low-level type with twice the size of the original, by
|
||||
/// doubling the size of the scalar type involved. For example `s32` will
|
||||
/// become `s64`, `<2 x s32>` will become `<2 x s64>`.
|
||||
LLT doubleScalarSize() const {
|
||||
assert(isSized() && "cannot change size of this type");
|
||||
return LLT{Kind, NumElements, ScalarSize * 2};
|
||||
}
|
||||
|
||||
/// \brief get a low-level type with half the size of the original, by halving
|
||||
/// the number of vector elements of the scalar type involved. The source must
|
||||
/// be a vector type with an even number of elements. For example `<4 x
|
||||
/// s32>` will become `<2 x s32>`, `<2 x s32>` will become `s32`.
|
||||
LLT halfElements() const {
|
||||
assert(isVector() && NumElements % 2 == 0 && "cannot half odd vector");
|
||||
if (NumElements == 2)
|
||||
return scalar(ScalarSize);
|
||||
|
||||
return LLT{Vector, static_cast<uint16_t>(NumElements / 2), ScalarSize};
|
||||
}
|
||||
|
||||
/// \brief get a low-level type with twice the size of the original, by
|
||||
/// doubling the number of vector elements of the scalar type involved. The
|
||||
/// source must be a vector type. For example `<2 x s32>` will become `<4 x
|
||||
/// s32>`. Doubling the number of elements in sN produces <2 x sN>.
|
||||
LLT doubleElements() const {
|
||||
return LLT{Vector, static_cast<uint16_t>(NumElements * 2), ScalarSize};
|
||||
}
|
||||
|
||||
void print(raw_ostream &OS) const;
|
||||
|
||||
bool operator ==(const LLT &RHS) const {
|
||||
return Kind == RHS.Kind && ScalarSize == RHS.ScalarSize &&
|
||||
NumElements == RHS.NumElements;
|
||||
}
|
||||
|
||||
friend struct DenseMapInfo<LLT>;
|
||||
private:
|
||||
unsigned ScalarSize;
|
||||
uint16_t NumElements;
|
||||
TypeKind Kind;
|
||||
};
|
||||
|
||||
template<> struct DenseMapInfo<LLT> {
|
||||
static inline LLT getEmptyKey() {
|
||||
return LLT{LLT::Invalid, 0, -1u};
|
||||
}
|
||||
static inline LLT getTombstoneKey() {
|
||||
return LLT{LLT::Invalid, 0, -2u};
|
||||
}
|
||||
static inline unsigned getHashValue(const LLT &Ty) {
|
||||
uint64_t Val = ((uint64_t)Ty.ScalarSize << 32) |
|
||||
((uint64_t)Ty.NumElements << 16) | (uint64_t)Ty.Kind;
|
||||
return DenseMapInfo<uint64_t>::getHashValue(Val);
|
||||
}
|
||||
static bool isEqual(const LLT &LHS, const LLT &RHS) {
|
||||
return LHS == RHS;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -23,6 +23,9 @@
|
|||
#include "llvm/ADT/iterator_range.h"
|
||||
#include "llvm/Analysis/AliasAnalysis.h"
|
||||
#include "llvm/CodeGen/MachineOperand.h"
|
||||
#ifdef LLVM_BUILD_GLOBAL_ISEL
|
||||
#include "llvm/CodeGen/LowLevelType.h"
|
||||
#endif
|
||||
#include "llvm/IR/DebugLoc.h"
|
||||
#include "llvm/IR/InlineAsm.h"
|
||||
#include "llvm/MC/MCInstrDesc.h"
|
||||
|
@ -39,9 +42,6 @@ class DIExpression;
|
|||
class TargetInstrInfo;
|
||||
class TargetRegisterClass;
|
||||
class TargetRegisterInfo;
|
||||
#ifdef LLVM_BUILD_GLOBAL_ISEL
|
||||
class Type;
|
||||
#endif
|
||||
class MachineFunction;
|
||||
class MachineMemOperand;
|
||||
|
||||
|
@ -108,9 +108,9 @@ private:
|
|||
|
||||
#ifdef LLVM_BUILD_GLOBAL_ISEL
|
||||
/// Type of the instruction in case of a generic opcode.
|
||||
/// \invariant This must be nullptr is getOpcode() is not
|
||||
/// \invariant This must be LLT{} if getOpcode() is not
|
||||
/// in the range of generic opcodes.
|
||||
Type *Ty;
|
||||
LLT Ty;
|
||||
#endif
|
||||
|
||||
MachineInstr(const MachineInstr&) = delete;
|
||||
|
@ -189,8 +189,8 @@ public:
|
|||
|
||||
/// Set the type of the instruction.
|
||||
/// \pre getOpcode() is in the range of the generic opcodes.
|
||||
void setType(Type *Ty);
|
||||
Type *getType() const;
|
||||
void setType(LLT Ty);
|
||||
LLT getType() const;
|
||||
|
||||
/// Return true if MI is in a bundle (but not the first MI in a bundle).
|
||||
///
|
||||
|
|
|
@ -48,6 +48,7 @@ add_llvm_library(LLVMCodeGen
|
|||
LiveVariables.cpp
|
||||
LLVMTargetMachine.cpp
|
||||
LocalStackSlotAllocation.cpp
|
||||
LowLevelType.cpp
|
||||
LowerEmuTLS.cpp
|
||||
MachineBasicBlock.cpp
|
||||
MachineBlockFrequencyInfo.cpp
|
||||
|
|
|
@ -69,7 +69,7 @@ bool IRTranslator::translateBinaryOp(unsigned Opcode, const Instruction &Inst) {
|
|||
unsigned Op0 = getOrCreateVReg(*Inst.getOperand(0));
|
||||
unsigned Op1 = getOrCreateVReg(*Inst.getOperand(1));
|
||||
unsigned Res = getOrCreateVReg(Inst);
|
||||
MIRBuilder.buildInstr(Opcode, Inst.getType(), Res, Op0, Op1);
|
||||
MIRBuilder.buildInstr(Opcode, LLT{*Inst.getType()}, Res, Op0, Op1);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -88,7 +88,7 @@ bool IRTranslator::translateBr(const Instruction &Inst) {
|
|||
if (BrInst.isUnconditional()) {
|
||||
const BasicBlock &BrTgt = *cast<BasicBlock>(BrInst.getOperand(0));
|
||||
MachineBasicBlock &TgtBB = getOrCreateBB(BrTgt);
|
||||
MIRBuilder.buildInstr(TargetOpcode::G_BR, BrTgt.getType(), TgtBB);
|
||||
MIRBuilder.buildInstr(TargetOpcode::G_BR, LLT{*BrTgt.getType()}, TgtBB);
|
||||
} else {
|
||||
assert(0 && "Not yet implemented");
|
||||
}
|
||||
|
|
|
@ -56,9 +56,9 @@ MachineBasicBlock::iterator MachineIRBuilder::getInsertPt() {
|
|||
//------------------------------------------------------------------------------
|
||||
// Build instruction variants.
|
||||
//------------------------------------------------------------------------------
|
||||
MachineInstr *MachineIRBuilder::buildInstr(unsigned Opcode, Type *Ty) {
|
||||
MachineInstr *MachineIRBuilder::buildInstr(unsigned Opcode, LLT Ty) {
|
||||
MachineInstr *NewMI = BuildMI(getMF(), DL, getTII().get(Opcode));
|
||||
if (Ty) {
|
||||
if (Ty.isValid()) {
|
||||
assert(isPreISelGenericOpcode(Opcode) &&
|
||||
"Only generic instruction can have a type");
|
||||
NewMI->setType(Ty);
|
||||
|
@ -71,10 +71,10 @@ MachineInstr *MachineIRBuilder::buildInstr(unsigned Opcode, Type *Ty) {
|
|||
|
||||
MachineInstr *MachineIRBuilder::buildInstr(unsigned Opcode, unsigned Res,
|
||||
unsigned Op0, unsigned Op1) {
|
||||
return buildInstr(Opcode, nullptr, Res, Op0, Op1);
|
||||
return buildInstr(Opcode, LLT{}, Res, Op0, Op1);
|
||||
}
|
||||
|
||||
MachineInstr *MachineIRBuilder::buildInstr(unsigned Opcode, Type *Ty,
|
||||
MachineInstr *MachineIRBuilder::buildInstr(unsigned Opcode, LLT Ty,
|
||||
unsigned Res, unsigned Op0,
|
||||
unsigned Op1) {
|
||||
MachineInstr *NewMI = buildInstr(Opcode, Ty);
|
||||
|
@ -87,16 +87,16 @@ MachineInstr *MachineIRBuilder::buildInstr(unsigned Opcode, Type *Ty,
|
|||
|
||||
MachineInstr *MachineIRBuilder::buildInstr(unsigned Opcode, unsigned Res,
|
||||
unsigned Op0) {
|
||||
MachineInstr *NewMI = buildInstr(Opcode, nullptr);
|
||||
MachineInstr *NewMI = buildInstr(Opcode, LLT{});
|
||||
MachineInstrBuilder(getMF(), NewMI).addReg(Res, RegState::Define).addReg(Op0);
|
||||
return NewMI;
|
||||
}
|
||||
|
||||
MachineInstr *MachineIRBuilder::buildInstr(unsigned Opcode) {
|
||||
return buildInstr(Opcode, nullptr);
|
||||
return buildInstr(Opcode, LLT{});
|
||||
}
|
||||
|
||||
MachineInstr *MachineIRBuilder::buildInstr(unsigned Opcode, Type *Ty,
|
||||
MachineInstr *MachineIRBuilder::buildInstr(unsigned Opcode, LLT Ty,
|
||||
MachineBasicBlock &BB) {
|
||||
MachineInstr *NewMI = buildInstr(Opcode, Ty);
|
||||
MachineInstrBuilder(getMF(), NewMI).addMBB(&BB);
|
||||
|
|
|
@ -65,8 +65,7 @@ void RegisterBankInfo::createRegisterBank(unsigned ID, const char *Name) {
|
|||
}
|
||||
|
||||
void RegisterBankInfo::addRegBankCoverage(unsigned ID, unsigned RCId,
|
||||
const TargetRegisterInfo &TRI,
|
||||
bool AddTypeMapping) {
|
||||
const TargetRegisterInfo &TRI) {
|
||||
RegisterBank &RB = getRegBank(ID);
|
||||
unsigned NbOfRegClasses = TRI.getNumRegClasses();
|
||||
|
||||
|
@ -98,13 +97,6 @@ void RegisterBankInfo::addRegBankCoverage(unsigned ID, unsigned RCId,
|
|||
// Remember the biggest size in bits.
|
||||
MaxSize = std::max(MaxSize, CurRC.getSize() * 8);
|
||||
|
||||
// If we have been asked to record the type supported by this
|
||||
// register bank, do it now.
|
||||
if (AddTypeMapping)
|
||||
for (MVT::SimpleValueType SVT :
|
||||
make_range(CurRC.vt_begin(), CurRC.vt_end()))
|
||||
recordRegBankForType(getRegBank(ID), SVT);
|
||||
|
||||
// Walk through all sub register classes and push them into the worklist.
|
||||
bool First = true;
|
||||
for (BitMaskClassIterator It(CurRC.getSubClassMask(), TRI); It.isValid();
|
||||
|
@ -240,30 +232,18 @@ RegisterBankInfo::getInstrMappingImpl(const MachineInstr &MI) const {
|
|||
// the register bank from the encoding constraints.
|
||||
CurRegBank = getRegBankFromConstraints(MI, OpIdx, TII, TRI);
|
||||
if (!CurRegBank) {
|
||||
// Check if we can deduce the register bank from the type of
|
||||
// the instruction.
|
||||
Type *MITy = MI.getType();
|
||||
if (MITy)
|
||||
CurRegBank = getRegBankForType(
|
||||
MVT::getVT(MITy, /*HandleUnknown*/ true).SimpleTy);
|
||||
if (!CurRegBank)
|
||||
// Use the current assigned register bank.
|
||||
// That may not make much sense though.
|
||||
CurRegBank = AltRegBank;
|
||||
if (!CurRegBank) {
|
||||
// All our attempts failed, give up.
|
||||
CompleteMapping = false;
|
||||
// All our attempts failed, give up.
|
||||
CompleteMapping = false;
|
||||
|
||||
if (!isCopyLike)
|
||||
// MI does not carry enough information to guess the mapping.
|
||||
return InstructionMapping();
|
||||
if (!isCopyLike)
|
||||
// MI does not carry enough information to guess the mapping.
|
||||
return InstructionMapping();
|
||||
|
||||
// For copies, we want to keep interating to find a register
|
||||
// bank for the other operands if we did not find one yet.
|
||||
if (RegBank)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
// For copies, we want to keep interating to find a register
|
||||
// bank for the other operands if we did not find one yet.
|
||||
if (RegBank)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
RegBank = CurRegBank;
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
//===-- llvm/CodeGen/GlobalISel/LowLevelType.cpp --------------------------===//
|
||||
//
|
||||
// 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 more header-heavy bits of the LLT class to
|
||||
/// avoid polluting users' namespaces.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/CodeGen/LowLevelType.h"
|
||||
#include "llvm/IR/DerivedTypes.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
using namespace llvm;
|
||||
|
||||
LLT::LLT(const Type &Ty) {
|
||||
if (auto VTy = dyn_cast<VectorType>(&Ty)) {
|
||||
ScalarSize = VTy->getElementType()->getPrimitiveSizeInBits();
|
||||
NumElements = VTy->getNumElements();
|
||||
Kind = NumElements == 1 ? Scalar : Vector;
|
||||
} else if (Ty.isSized()) {
|
||||
// Aggregates are no different from real scalars as far as GlobalISel is
|
||||
// concerned.
|
||||
Kind = Scalar;
|
||||
ScalarSize = Ty.getPrimitiveSizeInBits();
|
||||
NumElements = 1;
|
||||
} else {
|
||||
Kind = Unsized;
|
||||
ScalarSize = NumElements = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void LLT::print(raw_ostream &OS) const {
|
||||
if (isVector())
|
||||
OS << "<" << NumElements << " x s" << ScalarSize << ">";
|
||||
else if (isSized())
|
||||
OS << "s" << ScalarSize;
|
||||
else if (isValid())
|
||||
OS << "unsized";
|
||||
else
|
||||
llvm_unreachable("trying to print an invalid type");
|
||||
}
|
|
@ -173,14 +173,16 @@ static Cursor lexName(Cursor C, MIToken &Token, MIToken::TokenKind Type,
|
|||
return C;
|
||||
}
|
||||
|
||||
static Cursor maybeLexIntegerType(Cursor C, MIToken &Token) {
|
||||
if (C.peek() != 'i' || !isdigit(C.peek(1)))
|
||||
static Cursor maybeLexIntegerOrScalarType(Cursor C, MIToken &Token) {
|
||||
if ((C.peek() != 'i' && C.peek() != 's') || !isdigit(C.peek(1)))
|
||||
return None;
|
||||
char Kind = C.peek();
|
||||
auto Range = C;
|
||||
C.advance(); // Skip 'i'
|
||||
while (isdigit(C.peek()))
|
||||
C.advance();
|
||||
Token.reset(MIToken::IntegerType, Range.upto(C));
|
||||
Token.reset(Kind == 'i' ? MIToken::IntegerType : MIToken::ScalarType,
|
||||
Range.upto(C));
|
||||
return C;
|
||||
}
|
||||
|
||||
|
@ -566,7 +568,7 @@ StringRef llvm::lexMIToken(StringRef Source, MIToken &Token,
|
|||
return C.remaining();
|
||||
}
|
||||
|
||||
if (Cursor R = maybeLexIntegerType(C, Token))
|
||||
if (Cursor R = maybeLexIntegerOrScalarType(C, Token))
|
||||
return R.remaining();
|
||||
if (Cursor R = maybeLexMachineBasicBlock(C, Token, ErrorCallback))
|
||||
return R.remaining();
|
||||
|
|
|
@ -102,6 +102,7 @@ struct MIToken {
|
|||
NamedRegister,
|
||||
MachineBasicBlockLabel,
|
||||
MachineBasicBlock,
|
||||
ScalarType,
|
||||
StackObject,
|
||||
FixedStackObject,
|
||||
NamedGlobalValue,
|
||||
|
|
|
@ -130,10 +130,8 @@ public:
|
|||
bool parseIRConstant(StringRef::iterator Loc, StringRef Source,
|
||||
const Constant *&C);
|
||||
bool parseIRConstant(StringRef::iterator Loc, const Constant *&C);
|
||||
bool parseIRType(StringRef::iterator Loc, StringRef Source, unsigned &Read,
|
||||
Type *&Ty);
|
||||
// \p MustBeSized defines whether or not \p Ty must be sized.
|
||||
bool parseIRType(StringRef::iterator Loc, Type *&Ty, bool MustBeSized = true);
|
||||
bool parseLowLevelType(StringRef::iterator Loc, LLT &Ty,
|
||||
bool MustBeSized = true);
|
||||
bool parseTypedImmediateOperand(MachineOperand &Dest);
|
||||
bool parseFPImmediateOperand(MachineOperand &Dest);
|
||||
bool parseMBBReference(MachineBasicBlock *&MBB);
|
||||
|
@ -597,11 +595,11 @@ bool MIParser::parse(MachineInstr *&MI) {
|
|||
if (Token.isError() || parseInstruction(OpCode, Flags))
|
||||
return true;
|
||||
|
||||
Type *Ty = nullptr;
|
||||
LLT Ty{};
|
||||
if (isPreISelGenericOpcode(OpCode)) {
|
||||
// For generic opcode, a type is mandatory.
|
||||
auto Loc = Token.location();
|
||||
if (parseIRType(Loc, Ty))
|
||||
if (parseLowLevelType(Loc, Ty))
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -660,7 +658,7 @@ bool MIParser::parse(MachineInstr *&MI) {
|
|||
// TODO: Check for extraneous machine operands.
|
||||
MI = MF.CreateMachineInstr(MCID, DebugLocation, /*NoImplicit=*/true);
|
||||
MI->setFlags(Flags);
|
||||
if (Ty)
|
||||
if (Ty.isValid())
|
||||
MI->setType(Ty);
|
||||
for (const auto &Operand : Operands)
|
||||
MI->addOperand(MF, Operand.Operand);
|
||||
|
@ -1028,35 +1026,44 @@ bool MIParser::parseIRConstant(StringRef::iterator Loc, const Constant *&C) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool MIParser::parseIRType(StringRef::iterator Loc, StringRef StringValue,
|
||||
unsigned &Read, Type *&Ty) {
|
||||
auto Source = StringValue.str(); // The source has to be null terminated.
|
||||
SMDiagnostic Err;
|
||||
Ty = parseTypeAtBeginning(Source.c_str(), Read, Err,
|
||||
*MF.getFunction()->getParent(), &PFS.IRSlots);
|
||||
if (!Ty)
|
||||
return error(Loc + Err.getColumnNo(), Err.getMessage());
|
||||
return false;
|
||||
}
|
||||
bool MIParser::parseLowLevelType(StringRef::iterator Loc, LLT &Ty,
|
||||
bool MustBeSized) {
|
||||
if (Token.is(MIToken::Identifier) && Token.stringValue() == "unsized") {
|
||||
if (MustBeSized)
|
||||
return error(Loc, "expected sN or <N x sM> for sized GlobalISel type");
|
||||
lex();
|
||||
Ty = LLT::unsized();
|
||||
return false;
|
||||
} else if (Token.is(MIToken::ScalarType)) {
|
||||
Ty = LLT::scalar(APSInt(Token.range().drop_front()).getZExtValue());
|
||||
lex();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MIParser::parseIRType(StringRef::iterator Loc, Type *&Ty,
|
||||
bool MustBeSized) {
|
||||
// At this point we enter in the IR world, i.e., to get the correct type,
|
||||
// we need to hand off the whole string, not just the current token.
|
||||
// E.g., <4 x i64> would give '<' as a token and there is not much
|
||||
// the IR parser can do with that.
|
||||
unsigned Read = 0;
|
||||
if (parseIRType(Loc, StringRef(Loc), Read, Ty))
|
||||
return true;
|
||||
// The type must be sized, otherwise there is not much the backend
|
||||
// can do with it.
|
||||
if (MustBeSized && !Ty->isSized())
|
||||
return error("expected a sized type");
|
||||
// The next token is Read characters from the Loc.
|
||||
// However, the current location is not Loc, but Loc + the length of Token.
|
||||
// Therefore, subtract the length of Token (range().end() - Loc) to the
|
||||
// number of characters to skip before the next token.
|
||||
lex(Read - (Token.range().end() - Loc));
|
||||
// Now we're looking for a vector.
|
||||
if (Token.isNot(MIToken::less))
|
||||
return error(Loc, "expected unsized, sN or <N x sM> for GlobalISel type");
|
||||
lex();
|
||||
|
||||
if (Token.isNot(MIToken::IntegerLiteral))
|
||||
return error(Loc, "expected <N x sM> for vctor type");
|
||||
uint64_t NumElements = Token.integerValue().getZExtValue();
|
||||
lex();
|
||||
|
||||
if (Token.isNot(MIToken::Identifier) || Token.stringValue() != "x")
|
||||
return error(Loc, "expected '<N x sM>' for vector type");
|
||||
lex();
|
||||
|
||||
if (Token.isNot(MIToken::ScalarType))
|
||||
return error(Loc, "expected '<N x sM>' for vector type");
|
||||
uint64_t ScalarSize = APSInt(Token.range().drop_front()).getZExtValue();
|
||||
lex();
|
||||
|
||||
if (Token.isNot(MIToken::greater))
|
||||
return error(Loc, "expected '<N x sM>' for vector type");
|
||||
lex();
|
||||
|
||||
Ty = LLT::vector(NumElements, ScalarSize);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -565,9 +565,9 @@ void MIPrinter::print(const MachineInstr &MI) {
|
|||
OS << "frame-setup ";
|
||||
OS << TII->getName(MI.getOpcode());
|
||||
if (isPreISelGenericOpcode(MI.getOpcode())) {
|
||||
assert(MI.getType() && "Generic instructions must have a type");
|
||||
assert(MI.getType().isValid() && "Generic instructions must have a type");
|
||||
OS << ' ';
|
||||
MI.getType()->print(OS, /*IsForDebug*/ false, /*NoDetails*/ true);
|
||||
MI.getType().print(OS);
|
||||
}
|
||||
if (I < E)
|
||||
OS << ' ';
|
||||
|
|
|
@ -656,7 +656,7 @@ MachineInstr::MachineInstr(MachineFunction &MF, const MCInstrDesc &tid,
|
|||
debugLoc(std::move(dl))
|
||||
#ifdef LLVM_BUILD_GLOBAL_ISEL
|
||||
,
|
||||
Ty(nullptr)
|
||||
Ty(LLT{})
|
||||
#endif
|
||||
{
|
||||
assert(debugLoc.hasTrivialDestructor() && "Expected trivial destructor");
|
||||
|
@ -680,7 +680,7 @@ MachineInstr::MachineInstr(MachineFunction &MF, const MachineInstr &MI)
|
|||
MemRefs(MI.MemRefs), debugLoc(MI.getDebugLoc())
|
||||
#ifdef LLVM_BUILD_GLOBAL_ISEL
|
||||
,
|
||||
Ty(nullptr)
|
||||
Ty(LLT{})
|
||||
#endif
|
||||
{
|
||||
assert(debugLoc.hasTrivialDestructor() && "Expected trivial destructor");
|
||||
|
@ -710,18 +710,18 @@ MachineRegisterInfo *MachineInstr::getRegInfo() {
|
|||
// The proper implementation is WIP and is tracked here:
|
||||
// PR26576.
|
||||
#ifndef LLVM_BUILD_GLOBAL_ISEL
|
||||
void MachineInstr::setType(Type *Ty) {}
|
||||
void MachineInstr::setType(LLT Ty) {}
|
||||
|
||||
Type *MachineInstr::getType() const { return nullptr; }
|
||||
LLT MachineInstr::getType() const { return LLT{}; }
|
||||
|
||||
#else
|
||||
void MachineInstr::setType(Type *Ty) {
|
||||
assert((!Ty || isPreISelGenericOpcode(getOpcode())) &&
|
||||
void MachineInstr::setType(LLT Ty) {
|
||||
assert((!Ty.isValid() || isPreISelGenericOpcode(getOpcode())) &&
|
||||
"Non generic instructions are not supposed to be typed");
|
||||
this->Ty = Ty;
|
||||
}
|
||||
|
||||
Type *MachineInstr::getType() const { return Ty; }
|
||||
LLT MachineInstr::getType() const { return Ty; }
|
||||
#endif // LLVM_BUILD_GLOBAL_ISEL
|
||||
|
||||
/// RemoveRegOperandsFromUseLists - Unlink all of the register operands in
|
||||
|
@ -1724,9 +1724,9 @@ void MachineInstr::print(raw_ostream &OS, ModuleSlotTracker &MST,
|
|||
else
|
||||
OS << "UNKNOWN";
|
||||
|
||||
if (getType()) {
|
||||
if (getType().isValid()) {
|
||||
OS << ' ';
|
||||
getType()->print(OS, /*IsForDebug*/ false, /*NoDetails*/ true);
|
||||
getType().print(OS);
|
||||
OS << ' ';
|
||||
}
|
||||
|
||||
|
|
|
@ -155,6 +155,7 @@ AArch64RegisterBankInfo::getInstrAlternativeMappings(
|
|||
void AArch64RegisterBankInfo::applyMappingImpl(
|
||||
const OperandsMapper &OpdMapper) const {
|
||||
switch (OpdMapper.getMI().getOpcode()) {
|
||||
case TargetOpcode::G_ADD:
|
||||
case TargetOpcode::G_OR: {
|
||||
// Those ID must match getInstrAlternativeMappings.
|
||||
assert((OpdMapper.getInstrMapping().getID() == 1 ||
|
||||
|
@ -166,3 +167,27 @@ void AArch64RegisterBankInfo::applyMappingImpl(
|
|||
llvm_unreachable("Don't know how to handle that operation");
|
||||
}
|
||||
}
|
||||
|
||||
RegisterBankInfo::InstructionMapping
|
||||
AArch64RegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
|
||||
RegisterBankInfo::InstructionMapping Mapping = getInstrMappingImpl(MI);
|
||||
if (Mapping.isValid())
|
||||
return Mapping;
|
||||
|
||||
// As a top-level guess, vectors go in FPRs, scalars in GPRs. Obviously this
|
||||
// won't work for normal floating-point types (or NZCV). When such
|
||||
// instructions exist we'll need to look at the MI's opcode.
|
||||
LLT Ty = MI.getType();
|
||||
unsigned BankID;
|
||||
if (Ty.isVector())
|
||||
BankID = AArch64::FPRRegBankID;
|
||||
else
|
||||
BankID = AArch64::GPRRegBankID;
|
||||
|
||||
Mapping = InstructionMapping{1, 1, MI.getNumOperands()};
|
||||
int Size = Ty.isSized() ? Ty.getSizeInBits() : 0;
|
||||
for (unsigned Idx = 0; Idx < MI.getNumOperands(); ++Idx)
|
||||
Mapping.setOperandMapping(Idx, Size, getRegBank(BankID));
|
||||
|
||||
return Mapping;
|
||||
}
|
||||
|
|
|
@ -64,6 +64,8 @@ public:
|
|||
/// Alternative in the sense different from getInstrMapping.
|
||||
InstructionMappings
|
||||
getInstrAlternativeMappings(const MachineInstr &MI) const override;
|
||||
|
||||
InstructionMapping getInstrMapping(const MachineInstr &MI) const override;
|
||||
};
|
||||
} // End llvm namespace.
|
||||
#endif
|
||||
|
|
|
@ -9,7 +9,7 @@ target triple = "aarch64-apple-ios"
|
|||
; CHECK: name: addi64
|
||||
; CHECK: [[ARG1:%[0-9]+]](64) = COPY %x0
|
||||
; CHECK-NEXT: [[ARG2:%[0-9]+]](64) = COPY %x1
|
||||
; CHECK-NEXT: [[RES:%[0-9]+]](64) = G_ADD i64 [[ARG1]], [[ARG2]]
|
||||
; CHECK-NEXT: [[RES:%[0-9]+]](64) = G_ADD s64 [[ARG1]], [[ARG2]]
|
||||
; CHECK-NEXT: %x0 = COPY [[RES]]
|
||||
; CHECK-NEXT: RET_ReallyLR implicit %x0
|
||||
define i64 @addi64(i64 %arg1, i64 %arg2) {
|
||||
|
@ -28,7 +28,7 @@ define i64 @addi64(i64 %arg1, i64 %arg2) {
|
|||
; CHECK-NEXT: successors: %[[END:[0-9a-zA-Z._-]+]]({{0x[a-f0-9]+ / 0x[a-f0-9]+}} = 100.00%)
|
||||
;
|
||||
; Check that we emit the correct branch.
|
||||
; CHECK: G_BR label %[[END]]
|
||||
; CHECK: G_BR unsized %[[END]]
|
||||
;
|
||||
; Check that end contains the return instruction.
|
||||
; CHECK: [[END]]:
|
||||
|
@ -43,7 +43,7 @@ end:
|
|||
; CHECK: name: ori64
|
||||
; CHECK: [[ARG1:%[0-9]+]](64) = COPY %x0
|
||||
; CHECK-NEXT: [[ARG2:%[0-9]+]](64) = COPY %x1
|
||||
; CHECK-NEXT: [[RES:%[0-9]+]](64) = G_OR i64 [[ARG1]], [[ARG2]]
|
||||
; CHECK-NEXT: [[RES:%[0-9]+]](64) = G_OR s64 [[ARG1]], [[ARG2]]
|
||||
; CHECK-NEXT: %x0 = COPY [[RES]]
|
||||
; CHECK-NEXT: RET_ReallyLR implicit %x0
|
||||
define i64 @ori64(i64 %arg1, i64 %arg2) {
|
||||
|
@ -54,7 +54,7 @@ define i64 @ori64(i64 %arg1, i64 %arg2) {
|
|||
; CHECK: name: ori32
|
||||
; CHECK: [[ARG1:%[0-9]+]](32) = COPY %w0
|
||||
; CHECK-NEXT: [[ARG2:%[0-9]+]](32) = COPY %w1
|
||||
; CHECK-NEXT: [[RES:%[0-9]+]](32) = G_OR i32 [[ARG1]], [[ARG2]]
|
||||
; CHECK-NEXT: [[RES:%[0-9]+]](32) = G_OR s32 [[ARG1]], [[ARG2]]
|
||||
; CHECK-NEXT: %w0 = COPY [[RES]]
|
||||
; CHECK-NEXT: RET_ReallyLR implicit %w0
|
||||
define i32 @ori32(i32 %arg1, i32 %arg2) {
|
||||
|
|
|
@ -68,8 +68,8 @@ registers:
|
|||
body: |
|
||||
bb.0.entry:
|
||||
liveins: %x0
|
||||
; CHECK: %0(32) = G_ADD i32 %x0
|
||||
%0(32) = G_ADD i32 %x0, %x0
|
||||
; CHECK: %0(32) = G_ADD s32 %w0
|
||||
%0(32) = G_ADD s32 %w0, %w0
|
||||
...
|
||||
|
||||
---
|
||||
|
@ -85,8 +85,8 @@ registers:
|
|||
body: |
|
||||
bb.0.entry:
|
||||
liveins: %d0
|
||||
; CHECK: %0(32) = G_ADD <2 x i32> %d0
|
||||
%0(32) = G_ADD <2 x i32> %d0, %d0
|
||||
; CHECK: %0(64) = G_ADD <2 x s32> %d0
|
||||
%0(64) = G_ADD <2 x s32> %d0, %d0
|
||||
...
|
||||
|
||||
---
|
||||
|
@ -107,9 +107,9 @@ body: |
|
|||
liveins: %s0, %x0
|
||||
; CHECK: %0(32) = COPY %s0
|
||||
; CHECK-NEXT: %2(32) = COPY %0
|
||||
; CHECK-NEXT: %1(32) = G_ADD i32 %2, %x0
|
||||
; CHECK-NEXT: %1(32) = G_ADD s32 %2, %w0
|
||||
%0(32) = COPY %s0
|
||||
%1(32) = G_ADD i32 %0, %x0
|
||||
%1(32) = G_ADD s32 %0, %w0
|
||||
...
|
||||
|
||||
# Check that we repair the assignment for %0 differently for both uses.
|
||||
|
@ -129,9 +129,9 @@ body: |
|
|||
; CHECK: %0(32) = COPY %s0
|
||||
; CHECK-NEXT: %2(32) = COPY %0
|
||||
; CHECK-NEXT: %3(32) = COPY %0
|
||||
; CHECK-NEXT: %1(32) = G_ADD i32 %2, %3
|
||||
; CHECK-NEXT: %1(32) = G_ADD s32 %2, %3
|
||||
%0(32) = COPY %s0
|
||||
%1(32) = G_ADD i32 %0, %0
|
||||
%1(32) = G_ADD s32 %0, %0
|
||||
...
|
||||
|
||||
---
|
||||
|
@ -152,10 +152,10 @@ body: |
|
|||
bb.0.entry:
|
||||
liveins: %w0
|
||||
; CHECK: %0(32) = COPY %w0
|
||||
; CHECK-NEXT: %2(32) = G_ADD i32 %0, %w0
|
||||
; CHECK-NEXT: %2(32) = G_ADD s32 %0, %w0
|
||||
; CHECK-NEXT: %1(32) = COPY %2
|
||||
%0(32) = COPY %w0
|
||||
%1(32) = G_ADD i32 %0, %w0
|
||||
%1(32) = G_ADD s32 %0, %w0
|
||||
...
|
||||
|
||||
---
|
||||
|
@ -187,7 +187,7 @@ body: |
|
|||
|
||||
bb.1.then:
|
||||
successors: %bb.2.end
|
||||
%3(32) = G_ADD i32 %0, %0
|
||||
%3(32) = G_ADD s32 %0, %0
|
||||
|
||||
bb.2.end:
|
||||
%4(32) = PHI %0, %bb.0.entry, %3, %bb.1.then
|
||||
|
@ -211,9 +211,9 @@ body: |
|
|||
liveins: %w0, %s0
|
||||
; CHECK: %0(32) = COPY %w0
|
||||
; CHECK-NEXT: %2(32) = COPY %s0
|
||||
; CHECK-NEXT: %1(32) = G_ADD i32 %0, %2
|
||||
; CHECK-NEXT: %1(32) = G_ADD s32 %0, %2
|
||||
%0(32) = COPY %w0
|
||||
%1(32) = G_ADD i32 %0, %s0
|
||||
%1(32) = G_ADD s32 %0, %s0
|
||||
...
|
||||
|
||||
---
|
||||
|
@ -229,10 +229,10 @@ body: |
|
|||
bb.0.entry:
|
||||
liveins: %w0
|
||||
; CHECK: %0(32) = COPY %w0
|
||||
; CHECK-NEXT: %1(32) = G_ADD i32 %0, %0
|
||||
; CHECK-NEXT: %1(32) = G_ADD s32 %0, %0
|
||||
; CHECK-NEXT: %s0 = COPY %1
|
||||
%0(32) = COPY %w0
|
||||
%s0 = G_ADD i32 %0, %0
|
||||
%s0 = G_ADD s32 %0, %0
|
||||
...
|
||||
|
||||
---
|
||||
|
@ -271,13 +271,13 @@ body: |
|
|||
; FAST-NEXT: %3(64) = COPY %0
|
||||
; FAST-NEXT: %4(64) = COPY %1
|
||||
; The mapping of G_OR is on FPR.
|
||||
; FAST-NEXT: %2(64) = G_OR <2 x i32> %3, %4
|
||||
; FAST-NEXT: %2(64) = G_OR <2 x s32> %3, %4
|
||||
|
||||
; Greedy mode remapped the instruction on the GPR bank.
|
||||
; GREEDY-NEXT: %2(64) = G_OR <2 x i32> %0, %1
|
||||
; GREEDY-NEXT: %2(64) = G_OR <2 x s32> %0, %1
|
||||
%0(64) = COPY %x0
|
||||
%1(64) = COPY %x1
|
||||
%2(64) = G_OR <2 x i32> %0, %1
|
||||
%2(64) = G_OR <2 x s32> %0, %1
|
||||
...
|
||||
|
||||
---
|
||||
|
@ -317,13 +317,13 @@ body: |
|
|||
; FAST-NEXT: %3(64) = COPY %0
|
||||
; FAST-NEXT: %4(64) = COPY %1
|
||||
; The mapping of G_OR is on FPR.
|
||||
; FAST-NEXT: %2(64) = G_OR <2 x i32> %3, %4
|
||||
; FAST-NEXT: %2(64) = G_OR <2 x s32> %3, %4
|
||||
|
||||
; Greedy mode remapped the instruction on the GPR bank.
|
||||
; GREEDY-NEXT: %3(64) = G_OR <2 x i32> %0, %1
|
||||
; GREEDY-NEXT: %3(64) = G_OR <2 x s32> %0, %1
|
||||
; We need to keep %2 into FPR because we do not know anything about it.
|
||||
; GREEDY-NEXT: %2(64) = COPY %3
|
||||
%0(64) = COPY %x0
|
||||
%1(64) = COPY %x1
|
||||
%2(64) = G_OR <2 x i32> %0, %1
|
||||
%2(64) = G_OR <2 x s32> %0, %1
|
||||
...
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
; Tests for add.
|
||||
; CHECK: name: addi32
|
||||
; CHECK: G_ADD i32
|
||||
; CHECK: G_ADD s32
|
||||
define i32 @addi32(i32 %arg1, i32 %arg2) {
|
||||
%res = add i32 %arg1, %arg2
|
||||
ret i32 %res
|
||||
|
|
|
@ -10,6 +10,6 @@ registers:
|
|||
body: |
|
||||
bb.0.entry:
|
||||
liveins: %edi
|
||||
; CHECK: [[@LINE+1]]:16: expected a sized type
|
||||
%0 = G_ADD %opaque %edi, %edi
|
||||
; CHECK: [[@LINE+1]]:16: expected sN or <N x sM> for sized GlobalISel type
|
||||
%0 = G_ADD unsized %edi, %edi
|
||||
...
|
||||
|
|
|
@ -33,16 +33,16 @@ registers:
|
|||
body: |
|
||||
bb.0.entry:
|
||||
liveins: %edi
|
||||
; CHECK: %0(32) = G_ADD i32 %edi
|
||||
%0(32) = G_ADD i32 %edi, %edi
|
||||
; CHECK: %1(64) = G_ADD <2 x i32> %edi
|
||||
%1(64) = G_ADD <2 x i32> %edi, %edi
|
||||
; CHECK: %2(64) = G_ADD <2 x i32> %edi
|
||||
%2(64) = G_ADD %type_alias %edi, %edi
|
||||
; CHECK: %0(32) = G_ADD s32 %edi
|
||||
%0(32) = G_ADD s32 %edi, %edi
|
||||
; CHECK: %1(64) = G_ADD <2 x s32> %edi
|
||||
%1(64) = G_ADD <2 x s32> %edi, %edi
|
||||
; CHECK: %2(64) = G_ADD s64 %edi
|
||||
%2(64) = G_ADD s64 %edi, %edi
|
||||
; G_ADD is actually not a valid operand for structure type,
|
||||
; but that is the only one we have for now for testing.
|
||||
; CHECK: %3(64) = G_ADD { i32, i32 } %edi
|
||||
%3(64) = G_ADD {i32, i32} %edi, %edi
|
||||
; CHECK: %4(48) = G_ADD %structure_alias %edi
|
||||
%4(48) = G_ADD %structure_alias %edi, %edi
|
||||
; CHECK: %3(64) = G_ADD s64 %edi
|
||||
%3(64) = G_ADD s64 %edi, %edi
|
||||
; CHECK: %4(48) = G_ADD s48 %edi
|
||||
%4(48) = G_ADD s48 %edi, %edi
|
||||
...
|
||||
|
|
Loading…
Reference in New Issue