mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-01-14 15:38:57 +00:00
More of the Cell SPU code drop from "Team Aerospace".
llvm-svn: 44582
This commit is contained in:
parent
63d60199aa
commit
774da2e74c
654
lib/Target/CellSPU/SPUAsmPrinter.cpp
Normal file
654
lib/Target/CellSPU/SPUAsmPrinter.cpp
Normal file
@ -0,0 +1,654 @@
|
||||
//===-- SPUAsmPrinter.cpp - Print machine instrs to Cell SPU assembly -------=//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file was developed by a team from the Computer Systems Research
|
||||
// Department at The Aerospace Corporation.
|
||||
//
|
||||
// See README.txt for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains a printer that converts from our internal representation
|
||||
// of machine-dependent LLVM code to Cell SPU assembly language. This printer
|
||||
// is the output mechanism used by `llc'.
|
||||
//
|
||||
// Documentation at http://developer.apple.com/documentation/DeveloperTools/
|
||||
// Reference/Assembler/ASMIntroduction/chapter_1_section_1.html
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#define DEBUG_TYPE "asmprinter"
|
||||
#include "SPU.h"
|
||||
#include "SPUTargetMachine.h"
|
||||
#include "llvm/Constants.h"
|
||||
#include "llvm/DerivedTypes.h"
|
||||
#include "llvm/Module.h"
|
||||
#include "llvm/Assembly/Writer.h"
|
||||
#include "llvm/CodeGen/AsmPrinter.h"
|
||||
#include "llvm/CodeGen/DwarfWriter.h"
|
||||
#include "llvm/CodeGen/MachineModuleInfo.h"
|
||||
#include "llvm/CodeGen/MachineFunctionPass.h"
|
||||
#include "llvm/CodeGen/MachineInstr.h"
|
||||
#include "llvm/Support/Mangler.h"
|
||||
#include "llvm/Support/MathExtras.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/Target/TargetAsmInfo.h"
|
||||
#include "llvm/Target/MRegisterInfo.h"
|
||||
#include "llvm/Target/TargetInstrInfo.h"
|
||||
#include "llvm/Target/TargetOptions.h"
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include <set>
|
||||
using namespace llvm;
|
||||
|
||||
namespace {
|
||||
STATISTIC(EmittedInsts, "Number of machine instrs printed");
|
||||
|
||||
const std::string bss_section(".bss");
|
||||
|
||||
struct VISIBILITY_HIDDEN SPUAsmPrinter : public AsmPrinter {
|
||||
std::set<std::string> FnStubs, GVStubs;
|
||||
|
||||
SPUAsmPrinter(std::ostream &O, TargetMachine &TM, const TargetAsmInfo *T) :
|
||||
AsmPrinter(O, TM, T)
|
||||
{
|
||||
}
|
||||
|
||||
virtual const char *getPassName() const {
|
||||
return "STI CBEA SPU Assembly Printer";
|
||||
}
|
||||
|
||||
SPUTargetMachine &getTM() {
|
||||
return static_cast<SPUTargetMachine&>(TM);
|
||||
}
|
||||
|
||||
/// printInstruction - This method is automatically generated by tablegen
|
||||
/// from the instruction set description. This method returns true if the
|
||||
/// machine instruction was sufficiently described to print it, otherwise it
|
||||
/// returns false.
|
||||
bool printInstruction(const MachineInstr *MI);
|
||||
|
||||
void printMachineInstruction(const MachineInstr *MI);
|
||||
void printOp(const MachineOperand &MO);
|
||||
|
||||
/// printRegister - Print register according to target requirements.
|
||||
///
|
||||
void printRegister(const MachineOperand &MO, bool R0AsZero) {
|
||||
unsigned RegNo = MO.getReg();
|
||||
assert(MRegisterInfo::isPhysicalRegister(RegNo) && "Not physreg??");
|
||||
O << TM.getRegisterInfo()->get(RegNo).Name;
|
||||
}
|
||||
|
||||
void printOperand(const MachineInstr *MI, unsigned OpNo) {
|
||||
const MachineOperand &MO = MI->getOperand(OpNo);
|
||||
if (MO.isRegister()) {
|
||||
assert(MRegisterInfo::isPhysicalRegister(MO.getReg())&&"Not physreg??");
|
||||
O << TM.getRegisterInfo()->get(MO.getReg()).Name;
|
||||
} else if (MO.isImmediate()) {
|
||||
O << MO.getImmedValue();
|
||||
} else {
|
||||
printOp(MO);
|
||||
}
|
||||
}
|
||||
|
||||
bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
|
||||
unsigned AsmVariant, const char *ExtraCode);
|
||||
bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,
|
||||
unsigned AsmVariant, const char *ExtraCode);
|
||||
|
||||
|
||||
void
|
||||
printS7ImmOperand(const MachineInstr *MI, unsigned OpNo)
|
||||
{
|
||||
int value = MI->getOperand(OpNo).getImmedValue();
|
||||
value = (value << (32 - 7)) >> (32 - 7);
|
||||
|
||||
assert((value >= -(1 << 8) && value <= (1 << 7) - 1)
|
||||
&& "Invalid s7 argument");
|
||||
O << value;
|
||||
}
|
||||
|
||||
void
|
||||
printU7ImmOperand(const MachineInstr *MI, unsigned OpNo)
|
||||
{
|
||||
unsigned int value = MI->getOperand(OpNo).getImmedValue();
|
||||
assert(value < (1 << 8) && "Invalid u7 argument");
|
||||
O << value;
|
||||
}
|
||||
|
||||
void
|
||||
printMemRegImmS7(const MachineInstr *MI, unsigned OpNo)
|
||||
{
|
||||
char value = MI->getOperand(OpNo).getImmedValue();
|
||||
O << (int) value;
|
||||
O << "(";
|
||||
printOperand(MI, OpNo+1);
|
||||
O << ")";
|
||||
}
|
||||
|
||||
void
|
||||
printS16ImmOperand(const MachineInstr *MI, unsigned OpNo)
|
||||
{
|
||||
O << (short) MI->getOperand(OpNo).getImmedValue();
|
||||
}
|
||||
|
||||
void
|
||||
printU16ImmOperand(const MachineInstr *MI, unsigned OpNo)
|
||||
{
|
||||
O << (unsigned short)MI->getOperand(OpNo).getImmedValue();
|
||||
}
|
||||
|
||||
void
|
||||
printU32ImmOperand(const MachineInstr *MI, unsigned OpNo)
|
||||
{
|
||||
O << (unsigned)MI->getOperand(OpNo).getImmedValue();
|
||||
}
|
||||
|
||||
void
|
||||
printMemRegReg(const MachineInstr *MI, unsigned OpNo) {
|
||||
// When used as the base register, r0 reads constant zero rather than
|
||||
// the value contained in the register. For this reason, the darwin
|
||||
// assembler requires that we print r0 as 0 (no r) when used as the base.
|
||||
const MachineOperand &MO = MI->getOperand(OpNo);
|
||||
O << TM.getRegisterInfo()->get(MO.getReg()).Name;
|
||||
O << ", ";
|
||||
printOperand(MI, OpNo+1);
|
||||
}
|
||||
|
||||
void
|
||||
printU18ImmOperand(const MachineInstr *MI, unsigned OpNo)
|
||||
{
|
||||
unsigned int value = MI->getOperand(OpNo).getImmedValue();
|
||||
assert(value <= (1 << 19) - 1 && "Invalid u18 argument");
|
||||
O << value;
|
||||
}
|
||||
|
||||
void
|
||||
printS10ImmOperand(const MachineInstr *MI, unsigned OpNo)
|
||||
{
|
||||
short value = (short) (((int) MI->getOperand(OpNo).getImmedValue() << 16)
|
||||
>> 16);
|
||||
assert((value >= -(1 << 9) && value <= (1 << 9) - 1)
|
||||
&& "Invalid s10 argument");
|
||||
O << value;
|
||||
}
|
||||
|
||||
void
|
||||
printU10ImmOperand(const MachineInstr *MI, unsigned OpNo)
|
||||
{
|
||||
short value = (short) (((int) MI->getOperand(OpNo).getImmedValue() << 16)
|
||||
>> 16);
|
||||
assert((value <= (1 << 10) - 1) && "Invalid u10 argument");
|
||||
O << value;
|
||||
}
|
||||
|
||||
void
|
||||
printMemRegImmS10(const MachineInstr *MI, unsigned OpNo)
|
||||
{
|
||||
const MachineOperand &MO = MI->getOperand(OpNo);
|
||||
assert(MO.isImmediate()
|
||||
&& "printMemRegImmS10 first operand is not immedate");
|
||||
printS10ImmOperand(MI, OpNo);
|
||||
O << "(";
|
||||
printOperand(MI, OpNo+1);
|
||||
O << ")";
|
||||
}
|
||||
|
||||
void
|
||||
printAddr256K(const MachineInstr *MI, unsigned OpNo)
|
||||
{
|
||||
/* Note: operand 1 is an offset or symbol name. Operand 2 is
|
||||
ignored. */
|
||||
if (MI->getOperand(OpNo).isImmediate()) {
|
||||
printS16ImmOperand(MI, OpNo);
|
||||
} else {
|
||||
printOp(MI->getOperand(OpNo));
|
||||
}
|
||||
}
|
||||
|
||||
void printCallOperand(const MachineInstr *MI, unsigned OpNo) {
|
||||
printOp(MI->getOperand(OpNo));
|
||||
}
|
||||
|
||||
void printPCRelativeOperand(const MachineInstr *MI, unsigned OpNo) {
|
||||
printOp(MI->getOperand(OpNo));
|
||||
O << "-.";
|
||||
}
|
||||
|
||||
void printSymbolHi(const MachineInstr *MI, unsigned OpNo) {
|
||||
if (MI->getOperand(OpNo).isImmediate()) {
|
||||
printS16ImmOperand(MI, OpNo);
|
||||
} else {
|
||||
printOp(MI->getOperand(OpNo));
|
||||
O << "@h";
|
||||
}
|
||||
}
|
||||
|
||||
void printSymbolLo(const MachineInstr *MI, unsigned OpNo) {
|
||||
if (MI->getOperand(OpNo).isImmediate()) {
|
||||
printS16ImmOperand(MI, OpNo);
|
||||
} else {
|
||||
printOp(MI->getOperand(OpNo));
|
||||
O << "@l";
|
||||
}
|
||||
}
|
||||
|
||||
/// Print local store address
|
||||
void printSymbolLSA(const MachineInstr *MI, unsigned OpNo) {
|
||||
printOp(MI->getOperand(OpNo));
|
||||
}
|
||||
|
||||
void printROTHNeg7Imm(const MachineInstr *MI, unsigned OpNo) {
|
||||
if (MI->getOperand(OpNo).isImmediate()) {
|
||||
int value = (int) MI->getOperand(OpNo).getImmedValue();
|
||||
assert((value >= 0 && value < 16)
|
||||
&& "Invalid negated immediate rotate 7-bit argument");
|
||||
O << -value;
|
||||
} else {
|
||||
assert(0 && "Invalid/non-immediate rotate amount in printRotateNeg7Imm");
|
||||
}
|
||||
}
|
||||
|
||||
void printROTNeg7Imm(const MachineInstr *MI, unsigned OpNo) {
|
||||
if (MI->getOperand(OpNo).isImmediate()) {
|
||||
int value = (int) MI->getOperand(OpNo).getImmedValue();
|
||||
assert((value >= 0 && value < 32)
|
||||
&& "Invalid negated immediate rotate 7-bit argument");
|
||||
O << -value;
|
||||
} else {
|
||||
assert(0 && "Invalid/non-immediate rotate amount in printRotateNeg7Imm");
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool runOnMachineFunction(MachineFunction &F) = 0;
|
||||
virtual bool doFinalization(Module &M) = 0;
|
||||
};
|
||||
|
||||
/// LinuxAsmPrinter - SPU assembly printer, customized for Linux
|
||||
struct VISIBILITY_HIDDEN LinuxAsmPrinter : public SPUAsmPrinter {
|
||||
|
||||
DwarfWriter DW;
|
||||
|
||||
LinuxAsmPrinter(std::ostream &O, SPUTargetMachine &TM,
|
||||
const TargetAsmInfo *T) :
|
||||
SPUAsmPrinter(O, TM, T),
|
||||
DW(O, this, T)
|
||||
{ }
|
||||
|
||||
virtual const char *getPassName() const {
|
||||
return "STI CBEA SPU Assembly Printer";
|
||||
}
|
||||
|
||||
bool runOnMachineFunction(MachineFunction &F);
|
||||
bool doInitialization(Module &M);
|
||||
bool doFinalization(Module &M);
|
||||
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
AU.setPreservesAll();
|
||||
AU.addRequired<MachineModuleInfo>();
|
||||
SPUAsmPrinter::getAnalysisUsage(AU);
|
||||
}
|
||||
|
||||
/// getSectionForFunction - Return the section that we should emit the
|
||||
/// specified function body into.
|
||||
virtual std::string getSectionForFunction(const Function &F) const;
|
||||
};
|
||||
} // end of anonymous namespace
|
||||
|
||||
// Include the auto-generated portion of the assembly writer
|
||||
#include "SPUGenAsmWriter.inc"
|
||||
|
||||
void SPUAsmPrinter::printOp(const MachineOperand &MO) {
|
||||
switch (MO.getType()) {
|
||||
case MachineOperand::MO_Immediate:
|
||||
cerr << "printOp() does not handle immediate values\n";
|
||||
abort();
|
||||
return;
|
||||
|
||||
case MachineOperand::MO_MachineBasicBlock:
|
||||
printBasicBlockLabel(MO.getMachineBasicBlock());
|
||||
return;
|
||||
case MachineOperand::MO_JumpTableIndex:
|
||||
O << TAI->getPrivateGlobalPrefix() << "JTI" << getFunctionNumber()
|
||||
<< '_' << MO.getJumpTableIndex();
|
||||
// FIXME: PIC relocation model
|
||||
return;
|
||||
case MachineOperand::MO_ConstantPoolIndex:
|
||||
O << TAI->getPrivateGlobalPrefix() << "CPI" << getFunctionNumber()
|
||||
<< '_' << MO.getConstantPoolIndex();
|
||||
return;
|
||||
case MachineOperand::MO_ExternalSymbol:
|
||||
// Computing the address of an external symbol, not calling it.
|
||||
if (TM.getRelocationModel() != Reloc::Static) {
|
||||
std::string Name(TAI->getGlobalPrefix()); Name += MO.getSymbolName();
|
||||
GVStubs.insert(Name);
|
||||
O << "L" << Name << "$non_lazy_ptr";
|
||||
return;
|
||||
}
|
||||
O << TAI->getGlobalPrefix() << MO.getSymbolName();
|
||||
return;
|
||||
case MachineOperand::MO_GlobalAddress: {
|
||||
// Computing the address of a global symbol, not calling it.
|
||||
GlobalValue *GV = MO.getGlobal();
|
||||
std::string Name = Mang->getValueName(GV);
|
||||
|
||||
// External or weakly linked global variables need non-lazily-resolved
|
||||
// stubs
|
||||
if (TM.getRelocationModel() != Reloc::Static) {
|
||||
if (((GV->isDeclaration() || GV->hasWeakLinkage() ||
|
||||
GV->hasLinkOnceLinkage()))) {
|
||||
GVStubs.insert(Name);
|
||||
O << "L" << Name << "$non_lazy_ptr";
|
||||
return;
|
||||
}
|
||||
}
|
||||
O << Name;
|
||||
|
||||
if (GV->hasExternalWeakLinkage())
|
||||
ExtWeakSymbols.insert(GV);
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
O << "<unknown operand type: " << MO.getType() << ">";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// PrintAsmOperand - Print out an operand for an inline asm expression.
|
||||
///
|
||||
bool SPUAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
|
||||
unsigned AsmVariant,
|
||||
const char *ExtraCode) {
|
||||
// Does this asm operand have a single letter operand modifier?
|
||||
if (ExtraCode && ExtraCode[0]) {
|
||||
if (ExtraCode[1] != 0) return true; // Unknown modifier.
|
||||
|
||||
switch (ExtraCode[0]) {
|
||||
default: return true; // Unknown modifier.
|
||||
case 'L': // Write second word of DImode reference.
|
||||
// Verify that this operand has two consecutive registers.
|
||||
if (!MI->getOperand(OpNo).isRegister() ||
|
||||
OpNo+1 == MI->getNumOperands() ||
|
||||
!MI->getOperand(OpNo+1).isRegister())
|
||||
return true;
|
||||
++OpNo; // Return the high-part.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printOperand(MI, OpNo);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SPUAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
|
||||
unsigned OpNo,
|
||||
unsigned AsmVariant,
|
||||
const char *ExtraCode) {
|
||||
if (ExtraCode && ExtraCode[0])
|
||||
return true; // Unknown modifier.
|
||||
printMemRegReg(MI, OpNo);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// printMachineInstruction -- Print out a single PowerPC MI in Darwin syntax
|
||||
/// to the current output stream.
|
||||
///
|
||||
void SPUAsmPrinter::printMachineInstruction(const MachineInstr *MI) {
|
||||
++EmittedInsts;
|
||||
printInstruction(MI);
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::string LinuxAsmPrinter::getSectionForFunction(const Function &F) const {
|
||||
switch (F.getLinkage()) {
|
||||
default: assert(0 && "Unknown linkage type!");
|
||||
case Function::ExternalLinkage:
|
||||
case Function::InternalLinkage: return TAI->getTextSection();
|
||||
case Function::WeakLinkage:
|
||||
case Function::LinkOnceLinkage:
|
||||
return ""; // Print nothing for the time being...
|
||||
}
|
||||
}
|
||||
|
||||
/// runOnMachineFunction - This uses the printMachineInstruction()
|
||||
/// method to print assembly for each instruction.
|
||||
///
|
||||
bool
|
||||
LinuxAsmPrinter::runOnMachineFunction(MachineFunction &MF)
|
||||
{
|
||||
DW.SetModuleInfo(&getAnalysis<MachineModuleInfo>());
|
||||
|
||||
SetupMachineFunction(MF);
|
||||
O << "\n\n";
|
||||
|
||||
// Print out constants referenced by the function
|
||||
EmitConstantPool(MF.getConstantPool());
|
||||
|
||||
// Print out labels for the function.
|
||||
const Function *F = MF.getFunction();
|
||||
|
||||
SwitchToTextSection(getSectionForFunction(*F).c_str(), F);
|
||||
EmitAlignment(3, F);
|
||||
|
||||
switch (F->getLinkage()) {
|
||||
default: assert(0 && "Unknown linkage type!");
|
||||
case Function::InternalLinkage: // Symbols default to internal.
|
||||
break;
|
||||
case Function::ExternalLinkage:
|
||||
O << "\t.global\t" << CurrentFnName << "\n"
|
||||
<< "\t.type\t" << CurrentFnName << ", @function\n";
|
||||
break;
|
||||
case Function::WeakLinkage:
|
||||
case Function::LinkOnceLinkage:
|
||||
O << "\t.global\t" << CurrentFnName << "\n";
|
||||
O << "\t.weak_definition\t" << CurrentFnName << "\n";
|
||||
break;
|
||||
}
|
||||
O << CurrentFnName << ":\n";
|
||||
|
||||
// Emit pre-function debug information.
|
||||
DW.BeginFunction(&MF);
|
||||
|
||||
// Print out code for the function.
|
||||
for (MachineFunction::const_iterator I = MF.begin(), E = MF.end();
|
||||
I != E; ++I) {
|
||||
// Print a label for the basic block.
|
||||
if (I != MF.begin()) {
|
||||
printBasicBlockLabel(I, true);
|
||||
O << '\n';
|
||||
}
|
||||
for (MachineBasicBlock::const_iterator II = I->begin(), E = I->end();
|
||||
II != E; ++II) {
|
||||
// Print the assembly for the instruction.
|
||||
O << "\t";
|
||||
printMachineInstruction(II);
|
||||
}
|
||||
}
|
||||
|
||||
O << "\t.size\t" << CurrentFnName << ",.-" << CurrentFnName << "\n";
|
||||
|
||||
// Print out jump tables referenced by the function.
|
||||
EmitJumpTableInfo(MF.getJumpTableInfo(), MF);
|
||||
|
||||
// Emit post-function debug information.
|
||||
DW.EndFunction();
|
||||
|
||||
// We didn't modify anything.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool LinuxAsmPrinter::doInitialization(Module &M) {
|
||||
bool Result = AsmPrinter::doInitialization(M);
|
||||
SwitchToTextSection(TAI->getTextSection());
|
||||
// Emit initial debug information.
|
||||
DW.BeginModule(&M);
|
||||
return Result;
|
||||
}
|
||||
|
||||
bool LinuxAsmPrinter::doFinalization(Module &M) {
|
||||
const TargetData *TD = TM.getTargetData();
|
||||
|
||||
// Print out module-level global variables here.
|
||||
for (Module::const_global_iterator I = M.global_begin(), E = M.global_end();
|
||||
I != E; ++I) {
|
||||
if (!I->hasInitializer()) continue; // External global require no code
|
||||
|
||||
// Check to see if this is a special global used by LLVM, if so, emit it.
|
||||
if (EmitSpecialLLVMGlobal(I))
|
||||
continue;
|
||||
|
||||
std::string name = Mang->getValueName(I);
|
||||
Constant *C = I->getInitializer();
|
||||
unsigned Size = TD->getTypeStoreSize(C->getType());
|
||||
unsigned Align = TD->getPreferredAlignmentLog(I);
|
||||
|
||||
if (C->isNullValue() && /* FIXME: Verify correct */
|
||||
(I->hasInternalLinkage() || I->hasWeakLinkage() ||
|
||||
I->hasLinkOnceLinkage() ||
|
||||
(I->hasExternalLinkage() && !I->hasSection()))) {
|
||||
if (Size == 0) Size = 1; // .comm Foo, 0 is undefined, avoid it.
|
||||
if (I->hasExternalLinkage()) {
|
||||
// External linkage globals -> .bss section
|
||||
// FIXME: Want to set the global variable's section so that
|
||||
// SwitchToDataSection emits the ".section" directive
|
||||
SwitchToDataSection("\t.section\t.bss", I);
|
||||
O << "\t.global\t" << name << '\n';
|
||||
O << "\t.align\t" << Align << '\n';
|
||||
O << "\t.type\t" << name << ", @object\n";
|
||||
O << "\t.size\t" << name << ", " << Size << '\n';
|
||||
O << name << ":\n";
|
||||
O << "\t.zero\t" << Size;
|
||||
} else if (I->hasInternalLinkage()) {
|
||||
SwitchToDataSection("\t.data", I);
|
||||
O << TAI->getLCOMMDirective() << name << "," << Size << "," << Align;
|
||||
} else {
|
||||
SwitchToDataSection("\t.data", I);
|
||||
O << ".comm " << name << "," << Size;
|
||||
}
|
||||
O << "\t\t# '" << I->getName() << "'\n";
|
||||
} else {
|
||||
switch (I->getLinkage()) {
|
||||
case GlobalValue::LinkOnceLinkage:
|
||||
case GlobalValue::WeakLinkage:
|
||||
O << "\t.global " << name << '\n'
|
||||
<< "\t.weak_definition " << name << '\n';
|
||||
SwitchToDataSection(".section __DATA,__datacoal_nt,coalesced", I);
|
||||
break;
|
||||
case GlobalValue::AppendingLinkage:
|
||||
// FIXME: appending linkage variables should go into a section of
|
||||
// their name or something. For now, just emit them as external.
|
||||
case GlobalValue::ExternalLinkage:
|
||||
// If external or appending, declare as a global symbol
|
||||
O << "\t.global " << name << "\n";
|
||||
// FALL THROUGH
|
||||
case GlobalValue::InternalLinkage:
|
||||
if (I->isConstant()) {
|
||||
const ConstantArray *CVA = dyn_cast<ConstantArray>(C);
|
||||
if (TAI->getCStringSection() && CVA && CVA->isCString()) {
|
||||
SwitchToDataSection(TAI->getCStringSection(), I);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SwitchToDataSection("\t.data", I);
|
||||
break;
|
||||
default:
|
||||
cerr << "Unknown linkage type!";
|
||||
abort();
|
||||
}
|
||||
|
||||
EmitAlignment(Align, I);
|
||||
O << name << ":\t\t\t\t# '" << I->getName() << "'\n";
|
||||
|
||||
// If the initializer is a extern weak symbol, remember to emit the weak
|
||||
// reference!
|
||||
if (const GlobalValue *GV = dyn_cast<GlobalValue>(C))
|
||||
if (GV->hasExternalWeakLinkage())
|
||||
ExtWeakSymbols.insert(GV);
|
||||
|
||||
EmitGlobalConstant(C);
|
||||
O << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
// Output stubs for dynamically-linked functions
|
||||
if (TM.getRelocationModel() == Reloc::PIC_) {
|
||||
for (std::set<std::string>::iterator i = FnStubs.begin(), e = FnStubs.end();
|
||||
i != e; ++i) {
|
||||
SwitchToTextSection(".section __TEXT,__picsymbolstub1,symbol_stubs,"
|
||||
"pure_instructions,32");
|
||||
EmitAlignment(4);
|
||||
O << "L" << *i << "$stub:\n";
|
||||
O << "\t.indirect_symbol " << *i << "\n";
|
||||
O << "\tmflr r0\n";
|
||||
O << "\tbcl 20,31,L0$" << *i << "\n";
|
||||
O << "L0$" << *i << ":\n";
|
||||
O << "\tmflr r11\n";
|
||||
O << "\taddis r11,r11,ha16(L" << *i << "$lazy_ptr-L0$" << *i << ")\n";
|
||||
O << "\tmtlr r0\n";
|
||||
O << "\tlwzu r12,lo16(L" << *i << "$lazy_ptr-L0$" << *i << ")(r11)\n";
|
||||
O << "\tmtctr r12\n";
|
||||
O << "\tbctr\n";
|
||||
SwitchToDataSection(".lazy_symbol_pointer");
|
||||
O << "L" << *i << "$lazy_ptr:\n";
|
||||
O << "\t.indirect_symbol " << *i << "\n";
|
||||
O << "\t.long dyld_stub_binding_helper\n";
|
||||
}
|
||||
} else {
|
||||
for (std::set<std::string>::iterator i = FnStubs.begin(), e = FnStubs.end();
|
||||
i != e; ++i) {
|
||||
SwitchToTextSection(".section __TEXT,__symbol_stub1,symbol_stubs,"
|
||||
"pure_instructions,16");
|
||||
EmitAlignment(4);
|
||||
O << "L" << *i << "$stub:\n";
|
||||
O << "\t.indirect_symbol " << *i << "\n";
|
||||
O << "\tlis r11,ha16(L" << *i << "$lazy_ptr)\n";
|
||||
O << "\tlwzu r12,lo16(L" << *i << "$lazy_ptr)(r11)\n";
|
||||
O << "\tmtctr r12\n";
|
||||
O << "\tbctr\n";
|
||||
SwitchToDataSection(".lazy_symbol_pointer");
|
||||
O << "L" << *i << "$lazy_ptr:\n";
|
||||
O << "\t.indirect_symbol " << *i << "\n";
|
||||
O << "\t.long dyld_stub_binding_helper\n";
|
||||
}
|
||||
}
|
||||
|
||||
O << "\n";
|
||||
|
||||
// Output stubs for external and common global variables.
|
||||
if (GVStubs.begin() != GVStubs.end()) {
|
||||
SwitchToDataSection(".non_lazy_symbol_pointer");
|
||||
for (std::set<std::string>::iterator I = GVStubs.begin(),
|
||||
E = GVStubs.end(); I != E; ++I) {
|
||||
O << "L" << *I << "$non_lazy_ptr:\n";
|
||||
O << "\t.indirect_symbol " << *I << "\n";
|
||||
O << "\t.long\t0\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Emit initial debug information.
|
||||
DW.EndModule();
|
||||
|
||||
// Emit ident information
|
||||
O << "\t.ident\t\"(llvm 1.9+) STI CBEA Cell SPU backend\"\n";
|
||||
|
||||
return AsmPrinter::doFinalization(M);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// createSPUCodePrinterPass - Returns a pass that prints the Cell SPU
|
||||
/// assembly code for a MachineFunction to the given output stream, in a format
|
||||
/// that the Linux SPU assembler can deal with.
|
||||
///
|
||||
FunctionPass *llvm::createSPUAsmPrinterPass(std::ostream &o,
|
||||
SPUTargetMachine &tm) {
|
||||
return new LinuxAsmPrinter(o, tm, tm.getTargetAsmInfo());
|
||||
}
|
||||
|
62
lib/Target/CellSPU/SPUCallingConv.td
Normal file
62
lib/Target/CellSPU/SPUCallingConv.td
Normal file
@ -0,0 +1,62 @@
|
||||
//===- SPUCallingConv.td - Calling Conventions for CellSPU ------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file was developed by a team from the Computer Systems Research
|
||||
// Department at The Aerospace Corporation.
|
||||
//
|
||||
// See README.txt for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This describes the calling conventions for the STI Cell SPU architecture.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// CCIfSubtarget - Match if the current subtarget has a feature F.
|
||||
class CCIfSubtarget<string F, CCAction A>
|
||||
: CCIf<!strconcat("State.getTarget().getSubtarget<PPCSubtarget>().", F), A>;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Return Value Calling Convention
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// Return-value convention for Cell SPU: Everything can be passed back via $3:
|
||||
def RetCC_SPU : CallingConv<[
|
||||
CCIfType<[i32], CCAssignToReg<[R3]>>,
|
||||
CCIfType<[i64], CCAssignToReg<[R3]>>,
|
||||
CCIfType<[f32, f64], CCAssignToReg<[R3]>>,
|
||||
CCIfType<[v16i8, v8i16, v4i32, v2i64, v4f32, v2f64], CCAssignToReg<[R3]>>
|
||||
]>;
|
||||
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// CellSPU Argument Calling Conventions
|
||||
// FIXME
|
||||
//===----------------------------------------------------------------------===//
|
||||
/*
|
||||
def CC_SPU : CallingConv<[
|
||||
// The first 8 integer arguments are passed in integer registers.
|
||||
CCIfType<[i32], CCAssignToReg<[R3, R4, R5, R6, R7, R8, R9, R10]>>,
|
||||
CCIfType<[i64], CCAssignToReg<[X3, X4, X5, X6, X7, X8, X9, X10]>>,
|
||||
|
||||
// SPU can pass back arguments in all
|
||||
CCIfType<[f32, f64], CCIfSubtarget<"isMachoABI()",
|
||||
CCAssignToReg<[F1, F2, F3, F4, F5, F6, F7, F8,F9,F10,F11,F12,F13]>>>,
|
||||
// Other sub-targets pass FP values in F1-10.
|
||||
CCIfType<[f32, f64], CCAssignToReg<[F1, F2, F3, F4, F5, F6, F7, F8, F9,F10]>>,
|
||||
|
||||
// The first 12 Vector arguments are passed in altivec registers.
|
||||
CCIfType<[v16i8, v8i16, v4i32, v4f32],
|
||||
CCAssignToReg<[V2, V3, V4, V5, V6, V7, V8, V9, V10,V11,V12,V13]>>
|
||||
*/
|
||||
/*
|
||||
// Integer/FP values get stored in stack slots that are 8 bytes in size and
|
||||
// 8-byte aligned if there are no more registers to hold them.
|
||||
CCIfType<[i32, i64, f32, f64], CCAssignToStack<8, 8>>,
|
||||
|
||||
// Vectors get 16-byte stack slots that are 16-byte aligned.
|
||||
CCIfType<[v16i8, v8i16, v4i32, v2i64, v4f32, v2f64],
|
||||
CCAssignToStack<16, 16>>*/
|
||||
]>;
|
||||
*/
|
32
lib/Target/CellSPU/SPUFrameInfo.cpp
Normal file
32
lib/Target/CellSPU/SPUFrameInfo.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
//===-- SPUTargetMachine.cpp - Define TargetMachine for Cell SPU ----------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file was developed by a team from the Computer Systems Research
|
||||
// Department at The Aerospace Corporation.
|
||||
//
|
||||
// See README.txt for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Top-level implementation for the Cell SPU target.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "SPU.h"
|
||||
#include "SPUFrameInfo.h"
|
||||
#include "SPURegisterNames.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// SPUFrameInfo:
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
SPUFrameInfo::SPUFrameInfo(const TargetMachine &tm):
|
||||
TargetFrameInfo(TargetFrameInfo::StackGrowsDown, 16, 0),
|
||||
TM(tm)
|
||||
{
|
||||
LR[0].first = SPU::R0;
|
||||
LR[0].second = 16;
|
||||
}
|
77
lib/Target/CellSPU/SPUFrameInfo.h
Normal file
77
lib/Target/CellSPU/SPUFrameInfo.h
Normal file
@ -0,0 +1,77 @@
|
||||
//===-- SPUFrameInfo.h - Top-level interface for Cell SPU Target -*- C++ -*-==//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file was developed by a team from the Computer Systems Research
|
||||
// Department at The Aerospace Corporation.
|
||||
//
|
||||
// See README.txt for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains CellSPU frame information that doesn't fit anywhere else
|
||||
// cleanly...
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#if !defined(SPUFRAMEINFO_H)
|
||||
|
||||
#include "llvm/Target/TargetFrameInfo.h"
|
||||
#include "llvm/Target/TargetMachine.h"
|
||||
#include "SPURegisterInfo.h"
|
||||
|
||||
namespace llvm {
|
||||
class SPUFrameInfo: public TargetFrameInfo {
|
||||
const TargetMachine &TM;
|
||||
std::pair<unsigned, int> LR[1];
|
||||
|
||||
public:
|
||||
SPUFrameInfo(const TargetMachine &tm);
|
||||
|
||||
//! Return a function's saved spill slots
|
||||
/*!
|
||||
For CellSPU, a function's saved spill slots is just the link register.
|
||||
*/
|
||||
const std::pair<unsigned, int> *
|
||||
getCalleeSaveSpillSlots(unsigned &NumEntries) const;
|
||||
|
||||
//! Stack slot size (16 bytes)
|
||||
static const int stackSlotSize() {
|
||||
return 16;
|
||||
}
|
||||
//! Maximum frame offset representable by a signed 10-bit integer
|
||||
/*!
|
||||
This is the maximum frame offset that can be expressed as a 10-bit
|
||||
integer, used in D-form addresses.
|
||||
*/
|
||||
static const int maxFrameOffset() {
|
||||
return ((1 << 9) - 1) * stackSlotSize();
|
||||
}
|
||||
//! Minimum frame offset representable by a signed 10-bit integer
|
||||
static const int minFrameOffset() {
|
||||
return -(1 << 9) * stackSlotSize();
|
||||
}
|
||||
//! Minimum frame size (enough to spill LR + SP)
|
||||
static const int minStackSize() {
|
||||
return (2 * stackSlotSize());
|
||||
}
|
||||
//! Frame size required to spill all registers plus frame info
|
||||
static const int fullSpillSize() {
|
||||
return (SPURegisterInfo::getNumArgRegs() * stackSlotSize());
|
||||
}
|
||||
//! Number of instructions required to overcome hint-for-branch latency
|
||||
/*!
|
||||
HBR (hint-for-branch) instructions can be inserted when, for example,
|
||||
we know that a given function is going to be called, such as printf(),
|
||||
in the control flow graph. HBRs are only inserted if a sufficient number
|
||||
of instructions occurs between the HBR and the target. Currently, HBRs
|
||||
take 6 cycles, ergo, the magic number 6.
|
||||
*/
|
||||
static const int branchHintPenalty() {
|
||||
return 6;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#define SPUFRAMEINFO_H 1
|
||||
#endif
|
137
lib/Target/CellSPU/SPUHazardRecognizers.cpp
Normal file
137
lib/Target/CellSPU/SPUHazardRecognizers.cpp
Normal file
@ -0,0 +1,137 @@
|
||||
//===-- SPUHazardRecognizers.cpp - Cell Hazard Recognizer Impls -----------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file was developed by a team from the Computer Systems Research
|
||||
// Department at The Aerospace Corporation.
|
||||
//
|
||||
// See README.txt for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements hazard recognizers for scheduling on Cell SPU
|
||||
// processors.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#define DEBUG_TYPE "sched"
|
||||
|
||||
#include "SPUHazardRecognizers.h"
|
||||
#include "SPU.h"
|
||||
#include "SPUInstrInfo.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Cell SPU hazard recognizer
|
||||
//
|
||||
// This is the pipeline hazard recognizer for the Cell SPU processor. It does
|
||||
// very little right now.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
SPUHazardRecognizer::SPUHazardRecognizer(const TargetInstrInfo &tii) :
|
||||
TII(tii),
|
||||
EvenOdd(0)
|
||||
{
|
||||
}
|
||||
|
||||
/// Return the pipeline hazard type encountered or generated by this
|
||||
/// instruction. Currently returns NoHazard.
|
||||
///
|
||||
/// \return NoHazard
|
||||
HazardRecognizer::HazardType
|
||||
SPUHazardRecognizer::getHazardType(SDNode *Node)
|
||||
{
|
||||
// Initial thoughts on how to do this, but this code cannot work unless the
|
||||
// function's prolog and epilog code are also being scheduled so that we can
|
||||
// accurately determine which pipeline is being scheduled.
|
||||
#if 0
|
||||
HazardRecognizer::HazardType retval = NoHazard;
|
||||
bool mustBeOdd = false;
|
||||
|
||||
switch (Node->getOpcode()) {
|
||||
case SPU::LQDv16i8:
|
||||
case SPU::LQDv8i16:
|
||||
case SPU::LQDv4i32:
|
||||
case SPU::LQDv4f32:
|
||||
case SPU::LQDv2f64:
|
||||
case SPU::LQDr128:
|
||||
case SPU::LQDr64:
|
||||
case SPU::LQDr32:
|
||||
case SPU::LQDr16:
|
||||
case SPU::LQAv16i8:
|
||||
case SPU::LQAv8i16:
|
||||
case SPU::LQAv4i32:
|
||||
case SPU::LQAv4f32:
|
||||
case SPU::LQAv2f64:
|
||||
case SPU::LQAr128:
|
||||
case SPU::LQAr64:
|
||||
case SPU::LQAr32:
|
||||
case SPU::LQXv4i32:
|
||||
case SPU::LQXr128:
|
||||
case SPU::LQXr64:
|
||||
case SPU::LQXr32:
|
||||
case SPU::LQXr16:
|
||||
case SPU::STQDv16i8:
|
||||
case SPU::STQDv8i16:
|
||||
case SPU::STQDv4i32:
|
||||
case SPU::STQDv4f32:
|
||||
case SPU::STQDv2f64:
|
||||
case SPU::STQDr128:
|
||||
case SPU::STQDr64:
|
||||
case SPU::STQDr32:
|
||||
case SPU::STQDr16:
|
||||
case SPU::STQDr8:
|
||||
case SPU::STQAv16i8:
|
||||
case SPU::STQAv8i16:
|
||||
case SPU::STQAv4i32:
|
||||
case SPU::STQAv4f32:
|
||||
case SPU::STQAv2f64:
|
||||
case SPU::STQAr128:
|
||||
case SPU::STQAr64:
|
||||
case SPU::STQAr32:
|
||||
case SPU::STQAr16:
|
||||
case SPU::STQAr8:
|
||||
case SPU::STQXv16i8:
|
||||
case SPU::STQXv8i16:
|
||||
case SPU::STQXv4i32:
|
||||
case SPU::STQXv4f32:
|
||||
case SPU::STQXv2f64:
|
||||
case SPU::STQXr128:
|
||||
case SPU::STQXr64:
|
||||
case SPU::STQXr32:
|
||||
case SPU::STQXr16:
|
||||
case SPU::STQXr8:
|
||||
case SPU::RET:
|
||||
mustBeOdd = true;
|
||||
break;
|
||||
default:
|
||||
// Assume that this instruction can be on the even pipe
|
||||
break;
|
||||
}
|
||||
|
||||
if (mustBeOdd && !EvenOdd)
|
||||
retval = Hazard;
|
||||
|
||||
DOUT << "SPUHazardRecognizer EvenOdd " << EvenOdd << " Hazard " << retval << "\n";
|
||||
EvenOdd ^= 1;
|
||||
return retval;
|
||||
#else
|
||||
return NoHazard;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SPUHazardRecognizer::EmitInstruction(SDNode *Node)
|
||||
{
|
||||
}
|
||||
|
||||
void SPUHazardRecognizer::AdvanceCycle()
|
||||
{
|
||||
DOUT << "SPUHazardRecognizer::AdvanceCycle\n";
|
||||
}
|
||||
|
||||
void SPUHazardRecognizer::EmitNoop()
|
||||
{
|
||||
AdvanceCycle();
|
||||
}
|
43
lib/Target/CellSPU/SPUHazardRecognizers.h
Normal file
43
lib/Target/CellSPU/SPUHazardRecognizers.h
Normal file
@ -0,0 +1,43 @@
|
||||
//===-- SPUHazardRecognizers.h - Cell SPU Hazard Recognizer -----*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file was developed by a team from the Computer Systems Research
|
||||
// Department at The Aerospace Corporation.
|
||||
//
|
||||
// See README.txt for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines hazard recognizers for scheduling on the Cell SPU
|
||||
// processor.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SPUHAZRECS_H
|
||||
#define SPUHAZRECS_H
|
||||
|
||||
#include "llvm/CodeGen/ScheduleDAG.h"
|
||||
#include "SPUInstrInfo.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
/// SPUHazardRecognizer
|
||||
class SPUHazardRecognizer : public HazardRecognizer
|
||||
{
|
||||
private:
|
||||
const TargetInstrInfo &TII;
|
||||
int EvenOdd;
|
||||
|
||||
public:
|
||||
SPUHazardRecognizer(const TargetInstrInfo &TII);
|
||||
virtual HazardType getHazardType(SDNode *Node);
|
||||
virtual void EmitInstruction(SDNode *Node);
|
||||
virtual void AdvanceCycle();
|
||||
virtual void EmitNoop();
|
||||
};
|
||||
|
||||
} // end namespace llvm
|
||||
|
||||
#endif
|
||||
|
615
lib/Target/CellSPU/SPUISelDAGToDAG.cpp
Normal file
615
lib/Target/CellSPU/SPUISelDAGToDAG.cpp
Normal file
@ -0,0 +1,615 @@
|
||||
//===-- SPUISelDAGToDAG.cpp - CellSPU -pattern matching inst selector -----===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file was developed by a team from the Computer Systems Research
|
||||
// Department at The Aerospace Corporation.
|
||||
//
|
||||
// See README.txt for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines a pattern matching instruction selector for the Cell SPU,
|
||||
// converting from a legalized dag to a SPU-target dag.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "SPU.h"
|
||||
#include "SPUTargetMachine.h"
|
||||
#include "SPUISelLowering.h"
|
||||
#include "SPUHazardRecognizers.h"
|
||||
#include "SPUFrameInfo.h"
|
||||
#include "llvm/CodeGen/MachineConstantPool.h"
|
||||
#include "llvm/CodeGen/MachineInstrBuilder.h"
|
||||
#include "llvm/CodeGen/MachineFunction.h"
|
||||
#include "llvm/CodeGen/SSARegMap.h"
|
||||
#include "llvm/CodeGen/SelectionDAG.h"
|
||||
#include "llvm/CodeGen/SelectionDAGISel.h"
|
||||
#include "llvm/Target/TargetOptions.h"
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/Constants.h"
|
||||
#include "llvm/GlobalValue.h"
|
||||
#include "llvm/Intrinsics.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/MathExtras.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include <iostream>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace {
|
||||
//! ConstantSDNode predicate for i32 sign-extended, 10-bit immediates
|
||||
bool
|
||||
isI64IntS10Immediate(ConstantSDNode *CN)
|
||||
{
|
||||
return isS10Constant(CN->getValue());
|
||||
}
|
||||
|
||||
//! ConstantSDNode predicate for i32 sign-extended, 10-bit immediates
|
||||
bool
|
||||
isI32IntS10Immediate(ConstantSDNode *CN)
|
||||
{
|
||||
return isS10Constant((int) CN->getValue());
|
||||
}
|
||||
|
||||
#if 0
|
||||
//! SDNode predicate for sign-extended, 10-bit immediate values
|
||||
bool
|
||||
isI32IntS10Immediate(SDNode *N)
|
||||
{
|
||||
return (N->getOpcode() == ISD::Constant
|
||||
&& isI32IntS10Immediate(cast<ConstantSDNode>(N)));
|
||||
}
|
||||
#endif
|
||||
|
||||
//! ConstantSDNode predicate for i16 sign-extended, 10-bit immediate values
|
||||
bool
|
||||
isI16IntS10Immediate(ConstantSDNode *CN)
|
||||
{
|
||||
return isS10Constant((short) CN->getValue());
|
||||
}
|
||||
|
||||
//! SDNode predicate for i16 sign-extended, 10-bit immediate values
|
||||
bool
|
||||
isI16IntS10Immediate(SDNode *N)
|
||||
{
|
||||
return (N->getOpcode() == ISD::Constant
|
||||
&& isI16IntS10Immediate(cast<ConstantSDNode>(N)));
|
||||
}
|
||||
|
||||
//! ConstantSDNode predicate for signed 16-bit values
|
||||
/*!
|
||||
\arg CN The constant SelectionDAG node holding the value
|
||||
\arg Imm The returned 16-bit value, if returning true
|
||||
|
||||
This predicate tests the value in \a CN to see whether it can be
|
||||
represented as a 16-bit, sign-extended quantity. Returns true if
|
||||
this is the case.
|
||||
*/
|
||||
bool
|
||||
isIntS16Immediate(ConstantSDNode *CN, short &Imm)
|
||||
{
|
||||
MVT::ValueType vt = CN->getValueType(0);
|
||||
Imm = (short) CN->getValue();
|
||||
if (vt >= MVT::i1 && vt <= MVT::i16) {
|
||||
return true;
|
||||
} else if (vt == MVT::i32) {
|
||||
int32_t i_val = (int32_t) CN->getValue();
|
||||
short s_val = (short) i_val;
|
||||
return i_val == s_val;
|
||||
} else {
|
||||
int64_t i_val = (int64_t) CN->getValue();
|
||||
short s_val = (short) i_val;
|
||||
return i_val == s_val;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//! SDNode predicate for signed 16-bit values.
|
||||
bool
|
||||
isIntS16Immediate(SDNode *N, short &Imm)
|
||||
{
|
||||
return (N->getOpcode() == ISD::Constant
|
||||
&& isIntS16Immediate(cast<ConstantSDNode>(N), Imm));
|
||||
}
|
||||
|
||||
//! ConstantFPSDNode predicate for representing floats as 16-bit sign ext.
|
||||
static bool
|
||||
isFPS16Immediate(ConstantFPSDNode *FPN, short &Imm)
|
||||
{
|
||||
MVT::ValueType vt = FPN->getValueType(0);
|
||||
if (vt == MVT::f32) {
|
||||
const APFloat &apf = FPN->getValueAPF();
|
||||
float fval = apf.convertToFloat();
|
||||
int val = *((int *) &fval);
|
||||
int sval = (int) ((val << 16) >> 16);
|
||||
Imm = (short) val;
|
||||
return val == sval;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//===------------------------------------------------------------------===//
|
||||
//! MVT::ValueType to useful stuff structure:
|
||||
|
||||
struct valtype_map_s {
|
||||
MVT::ValueType VT;
|
||||
unsigned ldresult_ins; /// LDRESULT instruction (0 = undefined)
|
||||
int prefslot_byte; /// Byte offset of the "preferred" slot
|
||||
unsigned brcc_eq_ins; /// br_cc equal instruction
|
||||
unsigned brcc_neq_ins; /// br_cc not equal instruction
|
||||
};
|
||||
|
||||
const valtype_map_s valtype_map[] = {
|
||||
{ MVT::i1, 0, 3, 0, 0 },
|
||||
{ MVT::i8, 0, 3, 0, 0 },
|
||||
{ MVT::i16, SPU::ORHIr16, 2, SPU::BRHZ, SPU::BRHNZ },
|
||||
{ MVT::i32, SPU::ORIr32, 0, SPU::BRZ, SPU::BRNZ },
|
||||
{ MVT::i64, SPU::ORIr64, 0, 0, 0 },
|
||||
{ MVT::f32, SPU::ORIf32, 0, 0, 0 },
|
||||
{ MVT::f64, SPU::ORIf64, 0, 0, 0 }
|
||||
};
|
||||
|
||||
const size_t n_valtype_map = sizeof(valtype_map) / sizeof(valtype_map[0]);
|
||||
|
||||
const valtype_map_s *getValueTypeMapEntry(MVT::ValueType VT)
|
||||
{
|
||||
const valtype_map_s *retval = 0;
|
||||
for (size_t i = 0; i < n_valtype_map; ++i) {
|
||||
if (valtype_map[i].VT == VT) {
|
||||
retval = valtype_map + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (retval == 0) {
|
||||
cerr << "SPUISelDAGToDAG.cpp: getValueTypeMapEntry returns NULL for "
|
||||
<< MVT::getValueTypeString(VT)
|
||||
<< "\n";
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
/// SPUDAGToDAGISel - Cell SPU-specific code to select SPU machine
|
||||
/// instructions for SelectionDAG operations.
|
||||
///
|
||||
class SPUDAGToDAGISel :
|
||||
public SelectionDAGISel
|
||||
{
|
||||
SPUTargetMachine &TM;
|
||||
SPUTargetLowering &SPUtli;
|
||||
unsigned GlobalBaseReg;
|
||||
|
||||
public:
|
||||
SPUDAGToDAGISel(SPUTargetMachine &tm) :
|
||||
SelectionDAGISel(*tm.getTargetLowering()),
|
||||
TM(tm),
|
||||
SPUtli(*tm.getTargetLowering())
|
||||
{}
|
||||
|
||||
virtual bool runOnFunction(Function &Fn) {
|
||||
// Make sure we re-emit a set of the global base reg if necessary
|
||||
GlobalBaseReg = 0;
|
||||
SelectionDAGISel::runOnFunction(Fn);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// getI32Imm - Return a target constant with the specified value, of type
|
||||
/// i32.
|
||||
inline SDOperand getI32Imm(uint32_t Imm) {
|
||||
return CurDAG->getTargetConstant(Imm, MVT::i32);
|
||||
}
|
||||
|
||||
/// getI64Imm - Return a target constant with the specified value, of type
|
||||
/// i64.
|
||||
inline SDOperand getI64Imm(uint64_t Imm) {
|
||||
return CurDAG->getTargetConstant(Imm, MVT::i64);
|
||||
}
|
||||
|
||||
/// getSmallIPtrImm - Return a target constant of pointer type.
|
||||
inline SDOperand getSmallIPtrImm(unsigned Imm) {
|
||||
return CurDAG->getTargetConstant(Imm, SPUtli.getPointerTy());
|
||||
}
|
||||
|
||||
/// Select - Convert the specified operand from a target-independent to a
|
||||
/// target-specific node if it hasn't already been changed.
|
||||
SDNode *Select(SDOperand Op);
|
||||
|
||||
/// Return true if the address N is a RI7 format address [r+imm]
|
||||
bool SelectDForm2Addr(SDOperand Op, SDOperand N, SDOperand &Disp,
|
||||
SDOperand &Base);
|
||||
|
||||
//! Returns true if the address N is an A-form (local store) address
|
||||
bool SelectAFormAddr(SDOperand Op, SDOperand N, SDOperand &Base,
|
||||
SDOperand &Index);
|
||||
|
||||
//! D-form address predicate
|
||||
bool SelectDFormAddr(SDOperand Op, SDOperand N, SDOperand &Base,
|
||||
SDOperand &Index);
|
||||
|
||||
//! Address predicate if N can be expressed as an indexed [r+r] operation.
|
||||
bool SelectXFormAddr(SDOperand Op, SDOperand N, SDOperand &Base,
|
||||
SDOperand &Index);
|
||||
|
||||
/// SelectInlineAsmMemoryOperand - Implement addressing mode selection for
|
||||
/// inline asm expressions.
|
||||
virtual bool SelectInlineAsmMemoryOperand(const SDOperand &Op,
|
||||
char ConstraintCode,
|
||||
std::vector<SDOperand> &OutOps,
|
||||
SelectionDAG &DAG) {
|
||||
SDOperand Op0, Op1;
|
||||
switch (ConstraintCode) {
|
||||
default: return true;
|
||||
case 'm': // memory
|
||||
if (!SelectDFormAddr(Op, Op, Op0, Op1)
|
||||
&& !SelectAFormAddr(Op, Op, Op0, Op1))
|
||||
SelectXFormAddr(Op, Op, Op0, Op1);
|
||||
break;
|
||||
case 'o': // offsetable
|
||||
if (!SelectDFormAddr(Op, Op, Op0, Op1)
|
||||
&& !SelectAFormAddr(Op, Op, Op0, Op1)) {
|
||||
Op0 = Op;
|
||||
AddToISelQueue(Op0); // r+0.
|
||||
Op1 = getSmallIPtrImm(0);
|
||||
}
|
||||
break;
|
||||
case 'v': // not offsetable
|
||||
#if 1
|
||||
assert(0 && "InlineAsmMemoryOperand 'v' constraint not handled.");
|
||||
#else
|
||||
SelectAddrIdxOnly(Op, Op, Op0, Op1);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
OutOps.push_back(Op0);
|
||||
OutOps.push_back(Op1);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// InstructionSelectBasicBlock - This callback is invoked by
|
||||
/// SelectionDAGISel when it has created a SelectionDAG for us to codegen.
|
||||
virtual void InstructionSelectBasicBlock(SelectionDAG &DAG);
|
||||
|
||||
virtual const char *getPassName() const {
|
||||
return "Cell SPU DAG->DAG Pattern Instruction Selection";
|
||||
}
|
||||
|
||||
/// CreateTargetHazardRecognizer - Return the hazard recognizer to use for
|
||||
/// this target when scheduling the DAG.
|
||||
virtual HazardRecognizer *CreateTargetHazardRecognizer() {
|
||||
const TargetInstrInfo *II = SPUtli.getTargetMachine().getInstrInfo();
|
||||
assert(II && "No InstrInfo?");
|
||||
return new SPUHazardRecognizer(*II);
|
||||
}
|
||||
|
||||
// Include the pieces autogenerated from the target description.
|
||||
#include "SPUGenDAGISel.inc"
|
||||
};
|
||||
|
||||
/// InstructionSelectBasicBlock - This callback is invoked by
|
||||
/// SelectionDAGISel when it has created a SelectionDAG for us to codegen.
|
||||
void
|
||||
SPUDAGToDAGISel::InstructionSelectBasicBlock(SelectionDAG &DAG)
|
||||
{
|
||||
DEBUG(BB->dump());
|
||||
|
||||
// Select target instructions for the DAG.
|
||||
DAG.setRoot(SelectRoot(DAG.getRoot()));
|
||||
DAG.RemoveDeadNodes();
|
||||
|
||||
// Emit machine code to BB.
|
||||
ScheduleAndEmitDAG(DAG);
|
||||
}
|
||||
|
||||
bool
|
||||
SPUDAGToDAGISel::SelectDForm2Addr(SDOperand Op, SDOperand N, SDOperand &Disp,
|
||||
SDOperand &Base) {
|
||||
unsigned Opc = N.getOpcode();
|
||||
unsigned VT = N.getValueType();
|
||||
MVT::ValueType PtrVT = SPUtli.getPointerTy();
|
||||
ConstantSDNode *CN = 0;
|
||||
int Imm;
|
||||
|
||||
if (Opc == ISD::ADD) {
|
||||
SDOperand Op0 = N.getOperand(0);
|
||||
SDOperand Op1 = N.getOperand(1);
|
||||
if (Op1.getOpcode() == ISD::Constant ||
|
||||
Op1.getOpcode() == ISD::TargetConstant) {
|
||||
CN = cast<ConstantSDNode>(Op1);
|
||||
Imm = int(CN->getValue());
|
||||
if (Imm <= 0xff) {
|
||||
Disp = CurDAG->getTargetConstant(Imm, SPUtli.getPointerTy());
|
||||
Base = Op0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (Opc == ISD::GlobalAddress
|
||||
|| Opc == ISD::TargetGlobalAddress
|
||||
|| Opc == ISD::Register) {
|
||||
// Plain old local store address:
|
||||
Disp = CurDAG->getTargetConstant(0, VT);
|
||||
Base = N;
|
||||
return true;
|
||||
} else if (Opc == SPUISD::DFormAddr) {
|
||||
// D-Form address: This is pretty straightforward, naturally...
|
||||
CN = cast<ConstantSDNode>(N.getOperand(1));
|
||||
assert(CN != 0 && "SelectDFormAddr/SPUISD::DForm2Addr expecting constant");
|
||||
Imm = unsigned(CN->getValue());
|
||||
if (Imm < 0xff) {
|
||||
Disp = CurDAG->getTargetConstant(CN->getValue(), PtrVT);
|
||||
Base = N.getOperand(0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
\arg Op The ISD instructio operand
|
||||
\arg N The address to be tested
|
||||
\arg Base The base address
|
||||
\arg Index The base address index
|
||||
*/
|
||||
bool
|
||||
SPUDAGToDAGISel::SelectAFormAddr(SDOperand Op, SDOperand N, SDOperand &Base,
|
||||
SDOperand &Index) {
|
||||
// These match the addr256k operand type:
|
||||
MVT::ValueType PtrVT = SPUtli.getPointerTy();
|
||||
MVT::ValueType OffsVT = MVT::i16;
|
||||
|
||||
switch (N.getOpcode()) {
|
||||
case ISD::Constant:
|
||||
case ISD::TargetConstant: {
|
||||
// Loading from a constant address.
|
||||
ConstantSDNode *CN = dyn_cast<ConstantSDNode>(N);
|
||||
int Imm = (int)CN->getValue();
|
||||
if (Imm < 0x3ffff && (Imm & 0x3) == 0) {
|
||||
Base = CurDAG->getTargetConstant(Imm, PtrVT);
|
||||
// Note that this operand will be ignored by the assembly printer...
|
||||
Index = CurDAG->getTargetConstant(0, OffsVT);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
case ISD::ConstantPool:
|
||||
case ISD::TargetConstantPool: {
|
||||
// The constant pool address is N. Base is a dummy that will be ignored by
|
||||
// the assembly printer.
|
||||
Base = N;
|
||||
Index = CurDAG->getTargetConstant(0, OffsVT);
|
||||
return true;
|
||||
}
|
||||
|
||||
case ISD::GlobalAddress:
|
||||
case ISD::TargetGlobalAddress: {
|
||||
// The global address is N. Base is a dummy that is ignored by the
|
||||
// assembly printer.
|
||||
Base = N;
|
||||
Index = CurDAG->getTargetConstant(0, OffsVT);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
\arg Op The ISD instruction (ignored)
|
||||
\arg N The address to be tested
|
||||
\arg Base Base address register/pointer
|
||||
\arg Index Base address index
|
||||
|
||||
Examine the input address by a base register plus a signed 10-bit
|
||||
displacement, [r+I10] (D-form address).
|
||||
|
||||
\return true if \a N is a D-form address with \a Base and \a Index set
|
||||
to non-empty SDOperand instances.
|
||||
*/
|
||||
bool
|
||||
SPUDAGToDAGISel::SelectDFormAddr(SDOperand Op, SDOperand N, SDOperand &Base,
|
||||
SDOperand &Index) {
|
||||
unsigned Opc = N.getOpcode();
|
||||
unsigned PtrTy = SPUtli.getPointerTy();
|
||||
|
||||
if (Opc == ISD::Register) {
|
||||
Base = N;
|
||||
Index = CurDAG->getTargetConstant(0, PtrTy);
|
||||
return true;
|
||||
} else if (Opc == ISD::FrameIndex) {
|
||||
// Stack frame index must be less than 512 (divided by 16):
|
||||
FrameIndexSDNode *FI = dyn_cast<FrameIndexSDNode>(N);
|
||||
DEBUG(cerr << "SelectDFormAddr: ISD::FrameIndex = "
|
||||
<< FI->getIndex() << "\n");
|
||||
if (FI->getIndex() < SPUFrameInfo::maxFrameOffset()) {
|
||||
Base = CurDAG->getTargetConstant(0, PtrTy);
|
||||
Index = CurDAG->getTargetFrameIndex(FI->getIndex(), PtrTy);
|
||||
return true;
|
||||
}
|
||||
} else if (Opc == ISD::ADD) {
|
||||
// Generated by getelementptr
|
||||
const SDOperand Op0 = N.getOperand(0); // Frame index/base
|
||||
const SDOperand Op1 = N.getOperand(1); // Offset within base
|
||||
ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Op1);
|
||||
|
||||
// Not a constant?
|
||||
if (CN == 0)
|
||||
return false;
|
||||
|
||||
int32_t offset = (int32_t) CN->getSignExtended();
|
||||
unsigned Opc0 = Op0.getOpcode();
|
||||
|
||||
if ((offset & 0xf) != 0) {
|
||||
cerr << "SelectDFormAddr: unaligned offset = " << offset << "\n";
|
||||
abort();
|
||||
/*NOTREACHED*/
|
||||
}
|
||||
|
||||
if (Opc0 == ISD::FrameIndex) {
|
||||
FrameIndexSDNode *FI = dyn_cast<FrameIndexSDNode>(Op0);
|
||||
DEBUG(cerr << "SelectDFormAddr: ISD::ADD offset = " << offset
|
||||
<< " frame index = " << FI->getIndex() << "\n");
|
||||
|
||||
if (FI->getIndex() < SPUFrameInfo::maxFrameOffset()) {
|
||||
Base = CurDAG->getTargetConstant(offset, PtrTy);
|
||||
Index = CurDAG->getTargetFrameIndex(FI->getIndex(), PtrTy);
|
||||
return true;
|
||||
}
|
||||
} else if (offset > SPUFrameInfo::minFrameOffset()
|
||||
&& offset < SPUFrameInfo::maxFrameOffset()) {
|
||||
Base = CurDAG->getTargetConstant(offset, PtrTy);
|
||||
if (Opc0 == ISD::GlobalAddress) {
|
||||
// Convert global address to target global address
|
||||
GlobalAddressSDNode *GV = dyn_cast<GlobalAddressSDNode>(Op0);
|
||||
Index = CurDAG->getTargetGlobalAddress(GV->getGlobal(), PtrTy);
|
||||
return true;
|
||||
} else {
|
||||
// Otherwise, just take operand 0
|
||||
Index = Op0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (Opc == SPUISD::DFormAddr) {
|
||||
// D-Form address: This is pretty straightforward, naturally...
|
||||
ConstantSDNode *CN = cast<ConstantSDNode>(N.getOperand(1));
|
||||
assert(CN != 0 && "SelectDFormAddr/SPUISD::DFormAddr expecting constant");
|
||||
Base = CurDAG->getTargetConstant(CN->getValue(), PtrTy);
|
||||
Index = N.getOperand(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
\arg Op The ISD instruction operand
|
||||
\arg N The address operand
|
||||
\arg Base The base pointer operand
|
||||
\arg Index The offset/index operand
|
||||
|
||||
If the address \a N can be expressed as a [r + s10imm] address, returns false.
|
||||
Otherwise, creates two operands, Base and Index that will become the [r+r]
|
||||
address.
|
||||
*/
|
||||
bool
|
||||
SPUDAGToDAGISel::SelectXFormAddr(SDOperand Op, SDOperand N, SDOperand &Base,
|
||||
SDOperand &Index) {
|
||||
if (SelectAFormAddr(Op, N, Base, Index)
|
||||
|| SelectDFormAddr(Op, N, Base, Index))
|
||||
return false;
|
||||
|
||||
unsigned Opc = N.getOpcode();
|
||||
|
||||
if (Opc == ISD::ADD) {
|
||||
SDOperand N1 = N.getOperand(0);
|
||||
SDOperand N2 = N.getOperand(1);
|
||||
unsigned N1Opc = N1.getOpcode();
|
||||
unsigned N2Opc = N2.getOpcode();
|
||||
|
||||
if ((N1Opc == SPUISD::Hi && N2Opc == SPUISD::Lo)
|
||||
|| (N1Opc == SPUISD::Lo && N2Opc == SPUISD::Hi)) {
|
||||
Base = N.getOperand(0);
|
||||
Index = N.getOperand(1);
|
||||
return true;
|
||||
} else {
|
||||
cerr << "SelectXFormAddr: Unhandled ADD operands:\n";
|
||||
N1.Val->dump();
|
||||
cerr << "\n";
|
||||
N2.Val->dump();
|
||||
cerr << "\n";
|
||||
abort();
|
||||
/*UNREACHED*/
|
||||
}
|
||||
} else if (N.getNumOperands() == 2) {
|
||||
SDOperand N1 = N.getOperand(0);
|
||||
SDOperand N2 = N.getOperand(1);
|
||||
unsigned N1Opc = N1.getOpcode();
|
||||
unsigned N2Opc = N2.getOpcode();
|
||||
|
||||
if ((N1Opc == ISD::CopyToReg || N1Opc == ISD::Register)
|
||||
&& (N2Opc == ISD::CopyToReg || N2Opc == ISD::Register)) {
|
||||
Base = N.getOperand(0);
|
||||
Index = N.getOperand(1);
|
||||
return true;
|
||||
/*UNREACHED*/
|
||||
} else {
|
||||
cerr << "SelectXFormAddr: 2-operand unhandled operand:\n";
|
||||
N.Val->dump();
|
||||
cerr << "\n";
|
||||
abort();
|
||||
/*UNREACHED*/
|
||||
}
|
||||
} else {
|
||||
cerr << "SelectXFormAddr: Unhandled operand type:\n";
|
||||
N.Val->dump();
|
||||
cerr << "\n";
|
||||
abort();
|
||||
/*UNREACHED*/
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//! Convert the operand from a target-independent to a target-specific node
|
||||
/*!
|
||||
*/
|
||||
SDNode *
|
||||
SPUDAGToDAGISel::Select(SDOperand Op) {
|
||||
SDNode *N = Op.Val;
|
||||
unsigned Opc = N->getOpcode();
|
||||
|
||||
if (Opc >= ISD::BUILTIN_OP_END && Opc < SPUISD::FIRST_NUMBER) {
|
||||
return NULL; // Already selected.
|
||||
} else if (Opc == ISD::FrameIndex) {
|
||||
// Selects to AIr32 FI, 0 which in turn will become AIr32 SP, imm.
|
||||
int FI = cast<FrameIndexSDNode>(N)->getIndex();
|
||||
SDOperand TFI = CurDAG->getTargetFrameIndex(FI, SPUtli.getPointerTy());
|
||||
|
||||
DEBUG(cerr << "SPUDAGToDAGISel: Replacing FrameIndex with AI32 TFI, 0\n");
|
||||
return CurDAG->SelectNodeTo(N, SPU::AIr32, Op.getValueType(), TFI,
|
||||
CurDAG->getTargetConstant(0, MVT::i32));
|
||||
} else if (Opc == SPUISD::LDRESULT) {
|
||||
// Custom select instructions for LDRESULT
|
||||
unsigned VT = N->getValueType(0);
|
||||
SDOperand Arg = N->getOperand(0);
|
||||
SDOperand Chain = N->getOperand(1);
|
||||
SDOperand Zero = CurDAG->getTargetConstant(0, VT);
|
||||
SDNode *Result;
|
||||
const valtype_map_s *vtm = getValueTypeMapEntry(VT);
|
||||
|
||||
if (vtm->ldresult_ins == 0) {
|
||||
cerr << "LDRESULT for unsupported type: "
|
||||
<< MVT::getValueTypeString(VT)
|
||||
<< "\n";
|
||||
abort();
|
||||
} else
|
||||
Opc = vtm->ldresult_ins;
|
||||
|
||||
AddToISelQueue(Arg);
|
||||
AddToISelQueue(Zero);
|
||||
AddToISelQueue(Chain);
|
||||
Result = CurDAG->SelectNodeTo(N, Opc, VT, MVT::Other, Arg, Zero, Chain);
|
||||
Chain = SDOperand(Result, 1);
|
||||
return Result;
|
||||
}
|
||||
|
||||
return SelectCode(Op);
|
||||
}
|
||||
|
||||
/// createPPCISelDag - This pass converts a legalized DAG into a
|
||||
/// SPU-specific DAG, ready for instruction scheduling.
|
||||
///
|
||||
FunctionPass *llvm::createSPUISelDag(SPUTargetMachine &TM) {
|
||||
return new SPUDAGToDAGISel(TM);
|
||||
}
|
2673
lib/Target/CellSPU/SPUISelLowering.cpp
Normal file
2673
lib/Target/CellSPU/SPUISelLowering.cpp
Normal file
File diff suppressed because it is too large
Load Diff
139
lib/Target/CellSPU/SPUISelLowering.h
Normal file
139
lib/Target/CellSPU/SPUISelLowering.h
Normal file
@ -0,0 +1,139 @@
|
||||
//===-- SPUISelLowering.h - Cell SPU DAG Lowering Interface -----*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file was developed by a team from the Computer Systems Research
|
||||
// Department at The Aerospace Corporation.
|
||||
//
|
||||
// See README.txt for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines the interfaces that Cell SPU uses to lower LLVM code into
|
||||
// a selection DAG.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SPU_ISELLOWERING_H
|
||||
#define SPU_ISELLOWERING_H
|
||||
|
||||
#include "llvm/Target/TargetLowering.h"
|
||||
#include "llvm/CodeGen/SelectionDAG.h"
|
||||
#include "SPU.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace SPUISD {
|
||||
enum NodeType {
|
||||
// Start the numbering where the builting ops and target ops leave off.
|
||||
FIRST_NUMBER = ISD::BUILTIN_OP_END+SPU::INSTRUCTION_LIST_END,
|
||||
|
||||
// Pseudo instructions:
|
||||
RET_FLAG, ///< Return with flag, matched by bi instruction
|
||||
|
||||
Hi, ///< High address component (upper 16)
|
||||
Lo, ///< Low address component (lower 16)
|
||||
PCRelAddr, ///< Program counter relative address
|
||||
DFormAddr, ///< D-Form address "imm($r)"
|
||||
XFormAddr, ///< X-Form address "$r1($r2)"
|
||||
|
||||
LDRESULT, ///< Load result (value, chain)
|
||||
CALL, ///< CALL instruction
|
||||
SHUFB, ///< Vector shuffle (permute)
|
||||
INSERT_MASK, ///< Insert element shuffle mask
|
||||
CNTB, ///< Count leading ones in bytes
|
||||
PROMOTE_SCALAR, ///< Promote scalar->vector
|
||||
EXTRACT_ELT0, ///< Extract element 0
|
||||
EXTRACT_ELT0_CHAINED, ///< Extract element 0, with chain
|
||||
EXTRACT_I1_ZEXT, ///< Extract element 0 as i1, zero extend
|
||||
EXTRACT_I1_SEXT, ///< Extract element 0 as i1, sign extend
|
||||
EXTRACT_I8_ZEXT, ///< Extract element 0 as i8, zero extend
|
||||
EXTRACT_I8_SEXT, ///< Extract element 0 as i8, sign extend
|
||||
MPY, ///< 16-bit Multiply (low parts of a 32-bit)
|
||||
MPYU, ///< Multiply Unsigned
|
||||
MPYH, ///< Multiply High
|
||||
MPYHH, ///< Multiply High-High
|
||||
VEC_SHL, ///< Vector shift left
|
||||
VEC_SRL, ///< Vector shift right (logical)
|
||||
VEC_SRA, ///< Vector shift right (arithmetic)
|
||||
VEC_ROTL, ///< Vector rotate left
|
||||
VEC_ROTR, ///< Vector rotate right
|
||||
ROTBYTES_RIGHT_Z, ///< Vector rotate right, by bytes, zero fill
|
||||
ROTBYTES_RIGHT_S, ///< Vector rotate right, by bytes, sign fill
|
||||
ROTBYTES_LEFT, ///< Rotate bytes (loads -> ROTQBYI)
|
||||
ROTBYTES_LEFT_CHAINED, ///< Rotate bytes (loads -> ROTQBYI), with chain
|
||||
FSMBI, ///< Form Select Mask for Bytes, Immediate
|
||||
SELB, ///< Select bits -> (b & mask) | (a & ~mask)
|
||||
SFPConstant, ///< Single precision floating point constant
|
||||
FPInterp, ///< Floating point interpolate
|
||||
FPRecipEst, ///< Floating point reciprocal estimate
|
||||
SEXT32TO64, ///< Sign-extended 32-bit const -> 64-bits
|
||||
LAST_SPUISD ///< Last user-defined instruction
|
||||
};
|
||||
}
|
||||
|
||||
/// Predicates that are used for node matching:
|
||||
namespace SPU {
|
||||
SDOperand get_vec_u18imm(SDNode *N, SelectionDAG &DAG,
|
||||
MVT::ValueType ValueType);
|
||||
SDOperand get_vec_i16imm(SDNode *N, SelectionDAG &DAG,
|
||||
MVT::ValueType ValueType);
|
||||
SDOperand get_vec_i10imm(SDNode *N, SelectionDAG &DAG,
|
||||
MVT::ValueType ValueType);
|
||||
SDOperand get_vec_i8imm(SDNode *N, SelectionDAG &DAG,
|
||||
MVT::ValueType ValueType);
|
||||
SDOperand get_ILHUvec_imm(SDNode *N, SelectionDAG &DAG,
|
||||
MVT::ValueType ValueType);
|
||||
SDOperand get_v4i32_imm(SDNode *N, SelectionDAG &DAG);
|
||||
SDOperand get_v2i64_imm(SDNode *N, SelectionDAG &DAG);
|
||||
}
|
||||
|
||||
class SPUTargetMachine; // forward dec'l.
|
||||
|
||||
class SPUTargetLowering :
|
||||
public TargetLowering
|
||||
{
|
||||
int VarArgsFrameIndex; // FrameIndex for start of varargs area.
|
||||
int ReturnAddrIndex; // FrameIndex for return slot.
|
||||
SPUTargetMachine &SPUTM;
|
||||
|
||||
public:
|
||||
SPUTargetLowering(SPUTargetMachine &TM);
|
||||
|
||||
/// getTargetNodeName() - This method returns the name of a target specific
|
||||
/// DAG node.
|
||||
virtual const char *getTargetNodeName(unsigned Opcode) const;
|
||||
|
||||
/// LowerOperation - Provide custom lowering hooks for some operations.
|
||||
///
|
||||
virtual SDOperand LowerOperation(SDOperand Op, SelectionDAG &DAG);
|
||||
|
||||
virtual SDOperand PerformDAGCombine(SDNode *N, DAGCombinerInfo &DCI) const;
|
||||
|
||||
virtual void computeMaskedBitsForTargetNode(const SDOperand Op,
|
||||
uint64_t Mask,
|
||||
uint64_t &KnownZero,
|
||||
uint64_t &KnownOne,
|
||||
const SelectionDAG &DAG,
|
||||
unsigned Depth = 0) const;
|
||||
|
||||
virtual MachineBasicBlock *InsertAtEndOfBasicBlock(MachineInstr *MI,
|
||||
MachineBasicBlock *MBB);
|
||||
|
||||
ConstraintType getConstraintType(const std::string &ConstraintLetter) const;
|
||||
|
||||
std::pair<unsigned, const TargetRegisterClass*>
|
||||
getRegForInlineAsmConstraint(const std::string &Constraint,
|
||||
MVT::ValueType VT) const;
|
||||
|
||||
void LowerAsmOperandForConstraint(SDOperand Op, char ConstraintLetter,
|
||||
std::vector<SDOperand> &Ops,
|
||||
SelectionDAG &DAG);
|
||||
|
||||
/// isLegalAddressImmediate - Return true if the integer value can be used
|
||||
/// as the offset of the target addressing mode.
|
||||
virtual bool isLegalAddressImmediate(int64_t V, const Type *Ty) const;
|
||||
virtual bool isLegalAddressImmediate(GlobalValue *) const;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
52
lib/Target/CellSPU/SPUInstrBuilder.h
Normal file
52
lib/Target/CellSPU/SPUInstrBuilder.h
Normal file
@ -0,0 +1,52 @@
|
||||
//==-- SPUInstrBuilder.h - Aides for building Cell SPU insts -----*- C++ -*-==//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file exposes functions that may be used with BuildMI from the
|
||||
// MachineInstrBuilder.h file to simplify generating frame and constant pool
|
||||
// references.
|
||||
//
|
||||
// For reference, the order of operands for memory references is:
|
||||
// (Operand), Dest Reg, Base Reg, and either Reg Index or Immediate
|
||||
// Displacement.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SPU_INSTRBUILDER_H
|
||||
#define SPU_INSTRBUILDER_H
|
||||
|
||||
#include "llvm/CodeGen/MachineInstrBuilder.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
/// addFrameReference - This function is used to add a reference to the base of
|
||||
/// an abstract object on the stack frame of the current function. This
|
||||
/// reference has base register as the FrameIndex offset until it is resolved.
|
||||
/// This allows a constant offset to be specified as well...
|
||||
///
|
||||
inline const MachineInstrBuilder&
|
||||
addFrameReference(const MachineInstrBuilder &MIB, int FI, int Offset = 0,
|
||||
bool mem = true) {
|
||||
if (mem)
|
||||
return MIB.addImm(Offset).addFrameIndex(FI);
|
||||
else
|
||||
return MIB.addFrameIndex(FI).addImm(Offset);
|
||||
}
|
||||
|
||||
/// addConstantPoolReference - This function is used to add a reference to the
|
||||
/// base of a constant value spilled to the per-function constant pool. The
|
||||
/// reference has base register ConstantPoolIndex offset which is retained until
|
||||
/// either machine code emission or assembly output. This allows an optional
|
||||
/// offset to be added as well.
|
||||
///
|
||||
inline const MachineInstrBuilder&
|
||||
addConstantPoolReference(const MachineInstrBuilder &MIB, unsigned CPI,
|
||||
int Offset = 0) {
|
||||
return MIB.addImm(Offset).addConstantPoolIndex(CPI);
|
||||
}
|
||||
|
||||
} // End llvm namespace
|
||||
|
||||
#endif
|
308
lib/Target/CellSPU/SPUInstrFormats.td
Normal file
308
lib/Target/CellSPU/SPUInstrFormats.td
Normal file
@ -0,0 +1,308 @@
|
||||
//==== SPUInstrFormats.td - Cell SPU Instruction Formats ---*- tablegen -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file was developed by The Aerospace Corporation....
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Cell SPU instruction formats. Note that these are notationally similar to
|
||||
// PowerPC, like "A-Form". But the sizes of operands and fields differ.
|
||||
|
||||
// This was kiped from the PPC instruction formats (seemed like a good idea...)
|
||||
|
||||
class I<dag OOL, dag IOL, string asmstr, InstrItinClass itin>
|
||||
: Instruction {
|
||||
field bits<32> Inst;
|
||||
|
||||
let Name = "";
|
||||
let Namespace = "SPU";
|
||||
let OutOperandList = OOL;
|
||||
let InOperandList = IOL;
|
||||
let AsmString = asmstr;
|
||||
let Itinerary = itin;
|
||||
}
|
||||
|
||||
// RR Format
|
||||
class RRForm<bits<11> opcode, dag OOL, dag IOL, string asmstr,
|
||||
InstrItinClass itin, list<dag> pattern>
|
||||
: I<OOL, IOL, asmstr, itin> {
|
||||
bits<7> RA;
|
||||
bits<7> RB;
|
||||
bits<7> RT;
|
||||
|
||||
let Pattern = pattern;
|
||||
|
||||
let Inst{0-10} = opcode;
|
||||
let Inst{11-17} = RB;
|
||||
let Inst{18-24} = RA;
|
||||
let Inst{25-31} = RT;
|
||||
}
|
||||
|
||||
let RB = 0 in {
|
||||
// RR Format, where RB is zeroed (dont care):
|
||||
class RRForm_1<bits<11> opcode, dag OOL, dag IOL, string asmstr,
|
||||
InstrItinClass itin, list<dag> pattern>
|
||||
: RRForm<opcode, OOL, IOL, asmstr, itin, pattern>
|
||||
{ }
|
||||
|
||||
let RA = 0 in {
|
||||
// RR Format, where RA and RB are zeroed (dont care):
|
||||
// Used for reads from status control registers (see FPSCRRr32)
|
||||
class RRForm_2<bits<11> opcode, dag OOL, dag IOL, string asmstr,
|
||||
InstrItinClass itin, list<dag> pattern>
|
||||
: RRForm<opcode, OOL, IOL, asmstr, itin, pattern>
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
||||
let RT = 0 in {
|
||||
// RR Format, where RT is zeroed (don't care), or as the instruction handbook
|
||||
// says, "RT is a false target." Used in "Halt if" instructions
|
||||
class RRForm_3<bits<11> opcode, dag OOL, dag IOL, string asmstr,
|
||||
InstrItinClass itin, list<dag> pattern>
|
||||
: RRForm<opcode, OOL, IOL, asmstr, itin, pattern>
|
||||
{ }
|
||||
}
|
||||
|
||||
// RRR Format
|
||||
class RRRForm<bits<4> opcode, dag OOL, dag IOL, string asmstr,
|
||||
InstrItinClass itin, list<dag> pattern>
|
||||
: I<OOL, IOL, asmstr, itin>
|
||||
{
|
||||
bits<7> RA;
|
||||
bits<7> RB;
|
||||
bits<7> RC;
|
||||
bits<7> RT;
|
||||
|
||||
let Pattern = pattern;
|
||||
|
||||
let Inst{0-3} = opcode;
|
||||
let Inst{4-10} = RT;
|
||||
let Inst{11-17} = RB;
|
||||
let Inst{18-24} = RA;
|
||||
let Inst{25-31} = RC;
|
||||
}
|
||||
|
||||
// RI7 Format
|
||||
class RI7Form<bits<11> opcode, dag OOL, dag IOL, string asmstr,
|
||||
InstrItinClass itin, list<dag> pattern>
|
||||
: I<OOL, IOL, asmstr, itin>
|
||||
{
|
||||
bits<7> i7;
|
||||
bits<7> RA;
|
||||
bits<7> RT;
|
||||
|
||||
let Pattern = pattern;
|
||||
|
||||
let Inst{0-10} = opcode;
|
||||
let Inst{11-17} = i7;
|
||||
let Inst{18-24} = RA;
|
||||
let Inst{25-31} = RT;
|
||||
}
|
||||
|
||||
// CVTIntFp Format
|
||||
class CVTIntFPForm<bits<10> opcode, dag OOL, dag IOL, string asmstr,
|
||||
InstrItinClass itin, list<dag> pattern>
|
||||
: I<OOL, IOL, asmstr, itin>
|
||||
{
|
||||
bits<7> RA;
|
||||
bits<7> RT;
|
||||
|
||||
let Pattern = pattern;
|
||||
|
||||
let Inst{0-9} = opcode;
|
||||
let Inst{10-17} = 0;
|
||||
let Inst{18-24} = RA;
|
||||
let Inst{25-31} = RT;
|
||||
}
|
||||
|
||||
let RA = 0 in {
|
||||
class BICondForm<bits<11> opcode, string asmstr, list<dag> pattern>
|
||||
: RRForm<opcode, (outs), (ins R32C:$rA, R32C:$func), asmstr,
|
||||
BranchResolv, pattern>
|
||||
{ }
|
||||
|
||||
let RT = 0 in {
|
||||
// Branch instruction format (without D/E flag settings)
|
||||
class BRForm<bits<11> opcode, dag OOL, dag IOL, string asmstr,
|
||||
InstrItinClass itin, list<dag> pattern>
|
||||
: RRForm<opcode, OOL, IOL, asmstr, itin, pattern>
|
||||
{ }
|
||||
|
||||
class BIForm<bits<11> opcode, string asmstr, list<dag> pattern>
|
||||
: RRForm<opcode, (outs), (ins R32C:$func), asmstr, BranchResolv,
|
||||
pattern>
|
||||
{ }
|
||||
|
||||
let RB = 0 in {
|
||||
// Return instruction (bi, branch indirect), RA is zero (LR):
|
||||
class RETForm<string asmstr, list<dag> pattern>
|
||||
: BRForm<0b00010101100, (outs), (ins), asmstr, BranchResolv,
|
||||
pattern>
|
||||
{ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Branch indirect external data forms:
|
||||
class BISLEDForm<bits<2> DE_flag, string asmstr, list<dag> pattern>
|
||||
: I<(outs), (ins indcalltarget:$func), asmstr, BranchResolv>
|
||||
{
|
||||
bits<7> Rcalldest;
|
||||
|
||||
let Pattern = pattern;
|
||||
|
||||
let Inst{0-10} = 0b11010101100;
|
||||
let Inst{11} = 0;
|
||||
let Inst{12-13} = DE_flag;
|
||||
let Inst{14-17} = 0b0000;
|
||||
let Inst{18-24} = Rcalldest;
|
||||
let Inst{25-31} = 0b0000000;
|
||||
}
|
||||
|
||||
// RI10 Format
|
||||
class RI10Form<bits<8> opcode, dag OOL, dag IOL, string asmstr,
|
||||
InstrItinClass itin, list<dag> pattern>
|
||||
: I<OOL, IOL, asmstr, itin>
|
||||
{
|
||||
bits<10> i10;
|
||||
bits<7> RA;
|
||||
bits<7> RT;
|
||||
|
||||
let Pattern = pattern;
|
||||
|
||||
let Inst{0-7} = opcode;
|
||||
let Inst{8-17} = i10;
|
||||
let Inst{18-24} = RA;
|
||||
let Inst{25-31} = RT;
|
||||
}
|
||||
|
||||
// RI10 Format, where the constant is zero (or effectively ignored by the
|
||||
// SPU)
|
||||
class RI10Form_1<bits<8> opcode, dag OOL, dag IOL, string asmstr,
|
||||
InstrItinClass itin, list<dag> pattern>
|
||||
: I<OOL, IOL, asmstr, itin>
|
||||
{
|
||||
bits<7> RA;
|
||||
bits<7> RT;
|
||||
|
||||
let Pattern = pattern;
|
||||
|
||||
let Inst{0-7} = opcode;
|
||||
let Inst{8-17} = 0;
|
||||
let Inst{18-24} = RA;
|
||||
let Inst{25-31} = RT;
|
||||
}
|
||||
|
||||
// RI10 Format, where RT is ignored.
|
||||
// This format is used primarily by the Halt If ... Immediate set of
|
||||
// instructions
|
||||
class RI10Form_2<bits<8> opcode, dag OOL, dag IOL, string asmstr,
|
||||
InstrItinClass itin, list<dag> pattern>
|
||||
: I<OOL, IOL, asmstr, itin>
|
||||
{
|
||||
bits<10> i10;
|
||||
bits<7> RA;
|
||||
|
||||
let Pattern = pattern;
|
||||
|
||||
let Inst{0-7} = opcode;
|
||||
let Inst{8-17} = i10;
|
||||
let Inst{18-24} = RA;
|
||||
let Inst{25-31} = 0;
|
||||
}
|
||||
|
||||
// RI16 Format
|
||||
class RI16Form<bits<9> opcode, dag OOL, dag IOL, string asmstr,
|
||||
InstrItinClass itin, list<dag> pattern>
|
||||
: I<OOL, IOL, asmstr, itin>
|
||||
{
|
||||
bits<16> i16;
|
||||
bits<7> RT;
|
||||
|
||||
let Pattern = pattern;
|
||||
|
||||
let Inst{0-8} = opcode;
|
||||
let Inst{9-24} = i16;
|
||||
let Inst{25-31} = RT;
|
||||
}
|
||||
|
||||
// Specialized version of the RI16 Format for unconditional branch relative and
|
||||
// branch absolute, branch and set link. Note that for branch and set link, the
|
||||
// link register doesn't have to be $lr, but this is actually hard coded into
|
||||
// the instruction pattern.
|
||||
|
||||
let RT = 0 in {
|
||||
class UncondBranch<bits<9> opcode, dag OOL, dag IOL, string asmstr,
|
||||
list<dag> pattern>
|
||||
: RI16Form<opcode, OOL, IOL, asmstr, BranchResolv, pattern>
|
||||
{ }
|
||||
|
||||
class BranchSetLink<bits<9> opcode, dag OOL, dag IOL, string asmstr,
|
||||
list<dag> pattern>
|
||||
: RI16Form<opcode, OOL, IOL, asmstr, BranchResolv, pattern>
|
||||
{ }
|
||||
}
|
||||
|
||||
// RI18 Format
|
||||
class RI18Form<bits<7> opcode, dag OOL, dag IOL, string asmstr,
|
||||
InstrItinClass itin, list<dag> pattern>
|
||||
: I<OOL, IOL, asmstr, itin>
|
||||
{
|
||||
bits<18> i18;
|
||||
bits<7> RT;
|
||||
|
||||
let Pattern = pattern;
|
||||
|
||||
let Inst{0-6} = opcode;
|
||||
let Inst{7-24} = i18;
|
||||
let Inst{25-31} = RT;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Instruction formats for intrinsics:
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// RI10 Format for v8i16 intrinsics
|
||||
class RI10_Int_v8i16<bits<8> opcode, string opc, InstrItinClass itin,
|
||||
Intrinsic IntID> :
|
||||
RI10Form<opcode, (outs VECREG:$rT), (ins s10imm:$val, VECREG:$rA),
|
||||
!strconcat(opc, " $rT, $rA, $val"), itin,
|
||||
[(set (v8i16 VECREG:$rT), (IntID (v8i16 VECREG:$rA),
|
||||
i16ImmSExt10:$val))] >;
|
||||
|
||||
class RI10_Int_v4i32<bits<8> opcode, string opc, InstrItinClass itin,
|
||||
Intrinsic IntID> :
|
||||
RI10Form<opcode, (outs VECREG:$rT), (ins s10imm:$val, VECREG:$rA),
|
||||
!strconcat(opc, " $rT, $rA, $val"), itin,
|
||||
[(set (v4i32 VECREG:$rT), (IntID (v4i32 VECREG:$rA),
|
||||
i32ImmSExt10:$val))] >;
|
||||
|
||||
// RR Format for v8i16 intrinsics
|
||||
class RR_Int_v8i16<bits<11> opcode, string opc, InstrItinClass itin,
|
||||
Intrinsic IntID> :
|
||||
RRForm<opcode, (outs VECREG:$rT), (ins VECREG:$rA, VECREG:$rB),
|
||||
!strconcat(opc, " $rT, $rA, $rB"), itin,
|
||||
[(set (v8i16 VECREG:$rT), (IntID (v8i16 VECREG:$rA),
|
||||
(v8i16 VECREG:$rB)))] >;
|
||||
|
||||
// RR Format for v4i32 intrinsics
|
||||
class RR_Int_v4i32<bits<11> opcode, string opc, InstrItinClass itin,
|
||||
Intrinsic IntID> :
|
||||
RRForm<opcode, (outs VECREG:$rT), (ins VECREG:$rA, VECREG:$rB),
|
||||
!strconcat(opc, " $rT, $rA, $rB"), itin,
|
||||
[(set (v4i32 VECREG:$rT), (IntID (v4i32 VECREG:$rA),
|
||||
(v4i32 VECREG:$rB)))] >;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Pseudo instructions, like call frames:
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
class Pseudo<dag OOL, dag IOL, string asmstr, list<dag> pattern>
|
||||
: I<OOL, IOL, asmstr, NoItinerary> {
|
||||
let Pattern = pattern;
|
||||
let Inst{31-0} = 0;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user