Commit ac8da5cf by Jim Stichnoth

Implements simple returns and call args for Mips.

This patch is essentially the same as for ARM https://codereview.chromium.org/1127963004 I have incorporated the new 64 bit register work which was not available at the time of this earlier patch. The MIPS O32 Abi is not perfect on this patch but I am more or less following the development of the ARM patches and those were preliminary at this stage too. I will make corrections in a later patch when I incorporate more of the ARM patches. BUG= https://code.google.com/p/nativeclient/issues/detail?id=4167 R=stichnot@chromium.org Review URL: https://codereview.chromium.org/1416493002 .
parent 4c2153b1
......@@ -12,7 +12,6 @@
/// constructors and the dump()/emit() methods.
///
//===----------------------------------------------------------------------===//
#include "IceAssemblerMIPS32.h"
#include "IceCfg.h"
#include "IceCfgNode.h"
......@@ -21,14 +20,62 @@
#include "IceOperand.h"
#include "IceRegistersMIPS32.h"
#include "IceTargetLoweringMIPS32.h"
#include <limits>
namespace Ice {
bool OperandMIPS32Mem::canHoldOffset(Type Ty, bool SignExt, int32_t Offset) {
(void)SignExt;
(void)Ty;
if ((std::numeric_limits<int16_t>::min() <= Offset) &&
(Offset <= std::numeric_limits<int16_t>::max()))
return true;
return false;
}
OperandMIPS32Mem::OperandMIPS32Mem(Cfg *Func, Type Ty, Variable *Base,
ConstantInteger32 *ImmOffset, AddrMode Mode)
: OperandMIPS32(kMem, Ty), Base(Base), ImmOffset(ImmOffset), Mode(Mode) {
// The Neg modes are only needed for Reg +/- Reg.
(void)Func;
// assert(!isNegAddrMode());
NumVars = 1;
Vars = &this->Base;
}
const char *InstMIPS32::getWidthString(Type Ty) {
(void)Ty;
return "TBD";
}
template <> const char *InstMIPS32Addiu::Opcode = "addiu";
template <> const char *InstMIPS32Lui::Opcode = "lui";
template <> const char *InstMIPS32La::Opcode = "la";
template <> const char *InstMIPS32Ori::Opcode = "ori";
InstMIPS32Mov::InstMIPS32Mov(Cfg *Func, Variable *Dest, Operand *Src)
: InstMIPS32(Func, InstMIPS32::Mov, 2, Dest) {
auto *Dest64 = llvm::dyn_cast<Variable64On32>(Dest);
auto *Src64 = llvm::dyn_cast<Variable64On32>(Src);
assert(Dest64 == nullptr || Src64 == nullptr);
if (Dest64 != nullptr) {
// this-> is needed below because there is a parameter named Dest.
this->Dest = Dest64->getLo();
DestHi = Dest64->getHi();
}
if (Src64 == nullptr) {
addSource(Src);
} else {
addSource(Src64->getLo());
addSource(Src64->getHi());
}
}
InstMIPS32Ret::InstMIPS32Ret(Cfg *Func, Variable *RA, Variable *Source)
: InstMIPS32(Func, InstMIPS32::Ret, Source ? 2 : 1, nullptr) {
addSource(RA);
......@@ -46,6 +93,25 @@ void InstMIPS32::dump(const Cfg *Func) const {
Inst::dump(Func);
}
void OperandMIPS32Mem::emit(const Cfg *Func) const {
if (!BuildDefs::dump())
return;
llvm_unreachable("Not yet implemented");
(void)Func;
}
void InstMIPS32::emitUnaryopGPR(const char *Opcode, const InstMIPS32 *Inst,
const Cfg *Func) {
if (!BuildDefs::dump())
return;
Ostream &Str = Func->getContext()->getStrEmit();
// Type SrcTy = Inst->getSrc(0)->getType();
Str << "\t" << Opcode << "\t";
Inst->getDest()->emit(Func);
Str << ", ";
Inst->getSrc(0)->emit(Func);
}
void InstMIPS32Ret::emit(const Cfg *Func) const {
if (!BuildDefs::dump())
return;
......@@ -55,7 +121,7 @@ void InstMIPS32Ret::emit(const Cfg *Func) const {
assert(RA->getRegNum() == RegMIPS32::Reg_RA);
Ostream &Str = Func->getContext()->getStrEmit();
Str << "\t"
<< "jr $ra"
<< "jr"
<< "\t";
RA->emit(Func);
}
......@@ -73,4 +139,102 @@ void InstMIPS32Ret::dump(const Cfg *Func) const {
Str << "ret." << Ty << " ";
dumpSources(Func);
}
void InstMIPS32Mov::emit(const Cfg *Func) const {
if (!BuildDefs::dump())
return;
assert(!(isMultiDest() && isMultiSource()) && "Invalid vmov type.");
if (isMultiDest()) {
emitMultiDestSingleSource(Func);
return;
}
if (isMultiSource()) {
emitSingleDestMultiSource(Func);
return;
}
emitSingleDestSingleSource(Func);
}
void InstMIPS32Mov::emitIAS(const Cfg *Func) const {
assert(getSrcSize() == 1);
(void)Func;
llvm_unreachable("Not yet implemented");
}
void InstMIPS32Mov::dump(const Cfg *Func) const {
if (!BuildDefs::dump())
return;
assert(getSrcSize() == 1 || getSrcSize() == 2);
Ostream &Str = Func->getContext()->getStrDump();
Variable *Dest = getDest();
Variable *DestHi = getDestHi();
Dest->dump(Func);
if (DestHi) {
Str << ", ";
DestHi->dump(Func);
}
dumpOpcode(Str, " = mov", getDest()->getType());
Str << " ";
dumpSources(Func);
}
void InstMIPS32Mov::emitMultiDestSingleSource(const Cfg *Func) const {
if (!BuildDefs::dump())
return;
Ostream &Str = Func->getContext()->getStrEmit();
Variable *DestLo = getDest();
Variable *DestHi = getDestHi();
auto *Src = llvm::cast<Variable>(getSrc(0));
assert(DestHi->hasReg());
assert(DestLo->hasReg());
assert(llvm::isa<Variable>(Src) && Src->hasReg());
// Str << "\t"
// << "vmov" << getPredicate() << "\t";
DestLo->emit(Func);
Str << ", ";
DestHi->emit(Func);
Str << ", ";
Src->emit(Func);
}
void InstMIPS32Mov::emitSingleDestMultiSource(const Cfg *Func) const {
if (!BuildDefs::dump())
return;
Ostream &Str = Func->getContext()->getStrEmit();
Variable *Dest = getDest();
Variable *SrcLo = llvm::cast<Variable>(getSrc(0));
Variable *SrcHi = llvm::cast<Variable>(getSrc(1));
assert(SrcHi->hasReg());
assert(SrcLo->hasReg());
assert(Dest->hasReg());
assert(getSrcSize() == 2);
// Str << "\t"
// << "vmov" << getPredicate() << "\t";
Dest->emit(Func);
Str << ", ";
SrcLo->emit(Func);
Str << ", ";
SrcHi->emit(Func);
}
void InstMIPS32Mov::emitSingleDestSingleSource(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrEmit();
// assert(Inst->getSrcSize() == 1);
// Type SrcTy = Inst->getSrc(0)->getType();
Str << "\t"
<< "move"
<< "\t";
getDest()->emit(Func);
Str << ", ";
getSrc(0)->emit(Func);
}
} // end of namespace Ice
......@@ -18,61 +18,151 @@
// TODO(reed kotler). This needs to be scrubbed and is a placeholder to get
// the Mips skeleton in.
//
// ALIASESn is a family of macros that we use to define register aliasing in
// MIPS32. n indicates how many aliases are being provided to the macro. It
// assumes the parameters are register names declared in a namespace/class
// named RegMIPS32.
#ifndef ALIASES1
#define ALIASES1(r0) \
{RegMIPS32::r0}
#define ALIASES2(r0, r1) \
{RegMIPS32::r0, RegMIPS32::r1}
#define ALIASES3(r0, r1, r2) \
{RegMIPS32::r0, RegMIPS32::r1, RegMIPS32::r2}
#define ALIASES4(r0, r1, r2, r3) \
{RegMIPS32::r0, RegMIPS32::r1, RegMIPS32::r2, RegMIPS32::r3}
#define ALIASES7(r0, r1, r2, r3, r4, r5, r6) \
{RegMIPS32::r0, RegMIPS32::r1, RegMIPS32::r2, RegMIPS32::r3, RegMIPS32::r4,\
RegMIPS32::r5,RegMIPS32::r6}
#endif
#define REGMIPS32_GPR_TABLE \
/* val, encode, name, scratch, preserved, stackptr, frameptr, isInt, isFP */ \
X(Reg_ZERO, = 0, "zero", 0, 0, 0, 0, 0, 0) \
X(Reg_AT, = Reg_ZERO + 1, "at", 1, 0, 0, 0, 1, 0) \
X(Reg_V0, = Reg_ZERO + 2, "v0", 1, 0, 0, 0, 1, 0) \
X(Reg_V1, = Reg_ZERO + 3, "v1", 1, 0, 0, 0, 1, 0) \
X(Reg_A0, = Reg_ZERO + 4, "a0", 1, 0, 0, 0, 1, 0) \
X(Reg_A1, = Reg_ZERO + 5, "a1", 1, 0, 0, 0, 1, 0) \
X(Reg_A2, = Reg_ZERO + 6, "a2", 1, 0, 0, 0, 1, 0) \
X(Reg_A3, = Reg_ZERO + 7, "a3", 1, 0, 0, 0, 1, 0) \
X(Reg_T0, = Reg_ZERO + 8, "t0", 1, 0, 0, 0, 1, 0) \
X(Reg_T1, = Reg_ZERO + 9, "t1", 1, 0, 0, 0, 1, 0) \
X(Reg_T2, = Reg_ZERO + 10, "t2", 1, 0, 0, 0, 1, 0) \
X(Reg_T3, = Reg_ZERO + 11, "t3", 1, 0, 0, 0, 1, 0) \
X(Reg_T4, = Reg_ZERO + 12, "t4", 1, 0, 0, 0, 1, 0) \
X(Reg_T5, = Reg_ZERO + 14, "t5", 1, 0, 0, 0, 1, 0) \
X(Reg_T6, = Reg_ZERO + 14, "t6", 1, 0, 0, 0, 1, 0) \
X(Reg_T7, = Reg_ZERO + 15, "t7", 1, 0, 0, 0, 1, 0) \
X(Reg_S0, = Reg_ZERO + 16, "s0", 0, 1, 0, 0, 1, 0) \
X(Reg_S1, = Reg_ZERO + 17, "s1", 0, 1, 0, 0, 1, 0) \
X(Reg_S2, = Reg_ZERO + 18, "s2", 0, 1, 0, 0, 1, 0) \
X(Reg_S3, = Reg_ZERO + 19, "s3", 0, 1, 0, 0, 1, 0) \
X(Reg_S4, = Reg_ZERO + 20, "s4", 0, 1, 0, 0, 1, 0) \
X(Reg_S5, = Reg_ZERO + 21, "s5", 0, 1, 0, 0, 1, 0) \
X(Reg_S6, = Reg_ZERO + 22, "s6", 0, 1, 0, 0, 1, 0) \
X(Reg_S7, = Reg_ZERO + 23, "s7", 0, 1, 0, 0, 1, 0) \
X(Reg_T8, = Reg_ZERO + 23, "t8", 1, 0, 0, 0, 1, 0) \
X(Reg_T9, = Reg_ZERO + 25, "t9", 1, 0, 0, 0, 1, 0) \
X(Reg_K0, = Reg_ZERO + 26, "k0", 0, 0, 0, 0, 0, 0) \
X(Reg_K1, = Reg_ZERO + 27, "k1", 0, 0, 0, 0, 0, 0) \
X(Reg_GP, = Reg_ZERO + 28, "gp", 0, 0, 0, 0, 0, 0) \
X(Reg_SP, = Reg_ZERO + 29, "sp", 0, 0, 1, 0, 0, 0) \
X(Reg_FP, = Reg_ZERO + 30, "fp", 0, 0, 0, 1, 0, 0) \
X(Reg_RA, = Reg_ZERO + 31, "ra", 0, 1, 0, 0, 0, 0) \
/* val, encode, name, scratch, preserved, stackptr, frameptr, \
isInt, isI64Pair, isFP32, isFP64, isVec128, alias_init */ \
X(Reg_ZERO, = 0, "zero", 0, 0, 0, 0, 0, 0, 0, 0, 0, \
ALIASES1(Reg_ZERO)) \
X(Reg_AT, = Reg_ZERO + 1, "at", 0, 0, 0, 0, 1, 0, 0, 0, 0, \
ALIASES1(Reg_AT)) \
X(Reg_V0, = Reg_ZERO + 2, "v0", 1, 0, 0, 0, 1, 0, 0, 0, 0, \
ALIASES2(Reg_V0, Reg_V0V1)) \
X(Reg_V1, = Reg_ZERO + 3, "v1", 1, 0, 0, 0, 1, 0, 0, 0, 0, \
ALIASES2(Reg_V1, Reg_V0V1)) \
X(Reg_A0, = Reg_ZERO + 4, "a0", 1, 0, 0, 0, 1, 0, 0, 0, 0, \
ALIASES2(Reg_A0, Reg_A0A1)) \
X(Reg_A1, = Reg_ZERO + 5, "a1", 1, 0, 0, 0, 1, 0, 0, 0, 0, \
ALIASES2(Reg_A1, Reg_A0A1)) \
X(Reg_A2, = Reg_ZERO + 6, "a2", 1, 0, 0, 0, 1, 0, 0, 0, 0, \
ALIASES2(Reg_A2, Reg_A2A3)) \
X(Reg_A3, = Reg_ZERO + 7, "a3", 1, 0, 0, 0, 1, 0, 0, 0, 0, \
ALIASES2(Reg_A3, Reg_A2A3)) \
X(Reg_T0, = Reg_ZERO + 8, "t0", 1, 0, 0, 0, 1, 0, 0, 0, 0, \
ALIASES2(Reg_T0, Reg_T0T1)) \
X(Reg_T1, = Reg_ZERO + 9, "t1", 1, 0, 0, 0, 1, 0, 0, 0, 0, \
ALIASES2(Reg_T1, Reg_T0T1)) \
X(Reg_T2, = Reg_ZERO + 10, "t2", 1, 0, 0, 0, 1, 0, 0, 0, 0, \
ALIASES2(Reg_T2, Reg_T2T3)) \
X(Reg_T3, = Reg_ZERO + 11, "t3", 1, 0, 0, 0, 1, 0, 0, 0, 0, \
ALIASES2(Reg_T3, Reg_T2T3)) \
X(Reg_T4, = Reg_ZERO + 12, "t4", 1, 0, 0, 0, 1, 0, 0, 0, 0, \
ALIASES2(Reg_T4, Reg_T4T5)) \
X(Reg_T5, = Reg_ZERO + 14, "t5", 1, 0, 0, 0, 1, 0, 0, 0, 0, \
ALIASES2(Reg_T5, Reg_T4T5)) \
X(Reg_T6, = Reg_ZERO + 14, "t6", 1, 0, 0, 0, 1, 0, 0, 0, 0, \
ALIASES2(Reg_T6, Reg_T6T7)) \
X(Reg_T7, = Reg_ZERO + 15, "t7", 1, 0, 0, 0, 1, 0, 0, 0, 0, \
ALIASES2(Reg_T7, Reg_T6T7)) \
X(Reg_S0, = Reg_ZERO + 16, "s0", 0, 1, 0, 0, 1, 0, 0, 0, 0, \
ALIASES2(Reg_S0, Reg_S0S1)) \
X(Reg_S1, = Reg_ZERO + 17, "s1", 0, 1, 0, 0, 1, 0, 0, 0, 0, \
ALIASES2(Reg_S1, Reg_S0S1)) \
X(Reg_S2, = Reg_ZERO + 18, "s2", 0, 1, 0, 0, 1, 0, 0, 0, 0, \
ALIASES2(Reg_S2, Reg_S2S3)) \
X(Reg_S3, = Reg_ZERO + 19, "s3", 0, 1, 0, 0, 1, 0, 0, 0, 0, \
ALIASES2(Reg_S3, Reg_S2S3)) \
X(Reg_S4, = Reg_ZERO + 20, "s4", 0, 1, 0, 0, 1, 0, 0, 0, 0, \
ALIASES2(Reg_S4, Reg_S4S5)) \
X(Reg_S5, = Reg_ZERO + 21, "s5", 0, 1, 0, 0, 1, 0, 0, 0, 0, \
ALIASES2(Reg_S5, Reg_S4S5)) \
X(Reg_S6, = Reg_ZERO + 22, "s6", 0, 1, 0, 0, 1, 0, 0, 0, 0, \
ALIASES2(Reg_S6, Reg_S6S7)) \
X(Reg_S7, = Reg_ZERO + 23, "s7", 0, 1, 0, 0, 1, 0, 0, 0, 0, \
ALIASES2(Reg_S7, Reg_S6S7)) \
X(Reg_T8, = Reg_ZERO + 23, "t8", 1, 0, 0, 0, 1, 0, 0, 0, 0, \
ALIASES2(Reg_T8, Reg_T8T9)) \
X(Reg_T9, = Reg_ZERO + 25, "t9", 1, 0, 0, 0, 1, 0, 0, 0, 0, \
ALIASES2(Reg_T9, Reg_T8T9)) \
X(Reg_K0, = Reg_ZERO + 26, "k0", 0, 0, 0, 0, 0, 0, 0, 0, 0, \
ALIASES1(Reg_K0)) \
X(Reg_K1, = Reg_ZERO + 27, "k1", 0, 0, 0, 0, 0, 0, 0, 0, 0, \
ALIASES1(Reg_K1)) \
X(Reg_GP, = Reg_ZERO + 28, "gp", 0, 0, 0, 0, 0, 0, 0, 0, 0, \
ALIASES1(Reg_GP)) \
X(Reg_SP, = Reg_ZERO + 29, "sp", 0, 0, 1, 0, 0, 0, 0, 0, 0, \
ALIASES1(Reg_SP)) \
X(Reg_FP, = Reg_ZERO + 30, "fp", 0, 0, 0, 1, 0, 0, 0, 0, 0, \
ALIASES1(Reg_FP)) \
X(Reg_RA, = Reg_ZERO + 31, "ra", 0, 1, 0, 0, 0, 0, 0, 0, 0, \
ALIASES1(Reg_RA)) \
//#define X(val, encode, name, scratch, preserved, stackptr, frameptr,
// isInt, isFP)
// isInt, isI64Pair, isFP32, isFP64, isVec128, alias_init)
// TODO(reed kotler): List FP registers etc.
// Be able to grab even registers, and the corresponding odd register
// for each even register.
//#define X(val, encode, name, scratch, preserved, stackptr, frameptr,
// isInt, isI64Pair, isFP32, isFP64, isVec128, alias_init)
// The following defines a table with the available pairs of consecutive i32
// GPRs starting at an even GPR that is not r14. Those are used to hold i64
// variables for atomic memory operations. If one of the registers in the pair
// is preserved, then we mark the whole pair as preserved to help the register
// allocator.
#define REGMIPS32_I64PAIR_TABLE \
/* val, encode, name, scratch, preserved, stackptr, frameptr, \
isInt, isI64Pair, isFP32, isFP64, isVec128, alias_init */ \
X(Reg_V0V1, 0, "v0, v1", 1, 0, 0, 0, 0, 1, 0, 0, 0, \
ALIASES3(Reg_V0, Reg_V1, Reg_V0V1)) \
X(Reg_A0A1, 2, "a0, a1", 1, 0, 0, 0, 0, 1, 0, 0, 0, \
ALIASES3(Reg_A0, Reg_A1, Reg_A0A1)) \
X(Reg_A2A3, 4, "a2, a3", 1, 0, 0, 0, 0, 1, 0, 0, 0, \
ALIASES3(Reg_A2, Reg_A3, Reg_A2A3)) \
X(Reg_T0T1, 8, "t0, t1", 1, 0, 0, 0, 0, 1, 0, 0, 0, \
ALIASES3(Reg_T0, Reg_T1, Reg_T0T1)) \
X(Reg_T2T3, 10, "t2, t3", 1, 0, 0, 0, 0, 1, 0, 0, 0, \
ALIASES3(Reg_T2, Reg_T3, Reg_T2T3)) \
X(Reg_T4T5, 12,"t4, t5", 1, 0, 0, 0, 0, 1, 0, 0, 0, \
ALIASES3(Reg_T4, Reg_T5, Reg_T4T5)) \
X(Reg_T6T7, 14, "t6, t7", 1, 0, 0, 0, 0, 1, 0, 0, 0, \
ALIASES3(Reg_T6, Reg_T7, Reg_T6T7)) \
X(Reg_S0S1, 16, "s0, s1", 0, 1, 0, 0, 0, 1, 0, 0, 0, \
ALIASES3(Reg_S0, Reg_S1, Reg_S0S1)) \
X(Reg_S2S3, 18, "s2, s3", 0, 1, 0, 0, 0, 1, 0, 0, 0, \
ALIASES3(Reg_S2, Reg_S3, Reg_S2S3)) \
X(Reg_S4S5, 20, "s4, s5", 0, 1, 0, 0, 0, 1, 0, 0, 0, \
ALIASES3(Reg_S4, Reg_S5, Reg_S4S5)) \
X(Reg_S6S7, 22, "s6, s7", 0, 1, 0, 0, 0, 1, 0, 0, 0, \
ALIASES3(Reg_S6, Reg_S7, Reg_S6S7)) \
X(Reg_T8T9, 26, "t8, t9", 1, 0, 0, 0, 0, 1, 0, 0, 0, \
ALIASES3(Reg_T8, Reg_T9, Reg_T8T9)) \
//#define X(val, encode, name, scratch, preserved, stackptr, frameptr,
// isInt, isI64Pair, isFP32, isFP64, isVec128, alias_init)
// We also provide a combined table, so that there is a namespace where
// all of the registers are considered and have distinct numberings.
// This is in contrast to the above, where the "encode" is based on how
// the register numbers will be encoded in binaries and values can overlap.
#define REGMIPS32_TABLE \
/* val, encode, name, scratch, preserved, stackptr, frameptr, isInt, isFP */ \
REGMIPS32_GPR_TABLE
//#define X(val, encode, name, scratch, preserved, stackptr, frameptr,
// isInt, isFP)
#define REGMIPS32_TABLE \
/* val, encode, name, scratch, preserved, stackptr, frameptr, isInt, \
isFP32, isFP64, isVec128, alias_init */ \
REGMIPS32_GPR_TABLE \
REGMIPS32_I64PAIR_TABLE
//#define X(val, encode, name, scratch, preserved, stackptr, frameptr,
// isInt, isI64Pair, isFP32, isFP64, isVec128, alias_init)
#define REGMIPS32_TABLE_BOUNDS \
/* val, init */ \
X(Reg_GPR_First, = Reg_ZERO) \
X(Reg_GPR_Last, = Reg_RA)
X(Reg_GPR_Last, = Reg_RA) \
X(Reg_I64PAIR_First, = Reg_V0V1) \
X(Reg_I64PAIR_Last, = Reg_T8T9) \
//define X(val, init)
// TODO(reed kotler): add condition code tables, etc.
......
......@@ -26,6 +26,84 @@ namespace Ice {
class TargetMIPS32;
/// OperandMips32 extends the Operand hierarchy.
//
class OperandMIPS32 : public Operand {
OperandMIPS32() = delete;
OperandMIPS32(const OperandMIPS32 &) = delete;
OperandMIPS32 &operator=(const OperandMIPS32 &) = delete;
public:
enum OperandKindMIPS32 {
k__Start = Operand::kTarget,
kMem,
};
using Operand::dump;
void dump(const Cfg *, Ostream &Str) const override {
if (BuildDefs::dump())
Str << "<OperandMIPS32>";
}
protected:
OperandMIPS32(OperandKindMIPS32 Kind, Type Ty)
: Operand(static_cast<OperandKind>(Kind), Ty) {}
};
class OperandMIPS32Mem : public OperandMIPS32 {
OperandMIPS32Mem() = delete;
OperandMIPS32Mem(const OperandMIPS32Mem &) = delete;
OperandMIPS32Mem &operator=(const OperandMIPS32Mem &) = delete;
public:
/// Memory operand addressing mode.
/// The enum value also carries the encoding.
// TODO(jvoung): unify with the assembler.
enum AddrMode { Offset };
/// NOTE: The Variable-typed operands have to be registers.
///
/// Reg + Imm. The Immediate actually has a limited number of bits
/// for encoding, so check canHoldOffset first. It cannot handle
/// general Constant operands like ConstantRelocatable, since a relocatable
/// can potentially take up too many bits.
static OperandMIPS32Mem *create(Cfg *Func, Type Ty, Variable *Base,
ConstantInteger32 *ImmOffset,
AddrMode Mode = Offset) {
return new (Func->allocate<OperandMIPS32Mem>())
OperandMIPS32Mem(Func, Ty, Base, ImmOffset, Mode);
}
Variable *getBase() const { return Base; }
ConstantInteger32 *getOffset() const { return ImmOffset; }
AddrMode getAddrMode() const { return Mode; }
void emit(const Cfg *Func) const override;
using OperandMIPS32::dump;
static bool classof(const Operand *Operand) {
return Operand->getKind() == static_cast<OperandKind>(kMem);
}
/// Return true if a load/store instruction for an element of type Ty
/// can encode the Offset directly in the immediate field of the 32-bit
/// MIPS instruction. For some types, if the load is Sign extending, then
/// the range is reduced.
static bool canHoldOffset(Type Ty, bool SignExt, int32_t Offset);
void dump(const Cfg *Func, Ostream &Str) const override {
(void)Func;
(void)Str;
}
private:
OperandMIPS32Mem(Cfg *Func, Type Ty, Variable *Base,
ConstantInteger32 *ImmOffset, AddrMode Mode);
Variable *Base;
ConstantInteger32 *ImmOffset;
AddrMode Mode;
};
/// Base class for Mips instructions.
class InstMIPS32 : public InstTarget {
InstMIPS32() = delete;
......@@ -33,12 +111,28 @@ class InstMIPS32 : public InstTarget {
InstMIPS32 &operator=(const InstMIPS32 &) = delete;
public:
enum InstKindMIPS32 { k__Start = Inst::Target, Ret };
enum InstKindMIPS32 {
k__Start = Inst::Target,
Addiu,
La,
Lui,
Mov, // actually a pseudo op for addi rd, rs, 0
Ori,
Ret
};
static const char *getWidthString(Type Ty);
void dump(const Cfg *Func) const override;
void dumpOpcode(Ostream &Str, const char *Opcode, Type Ty) const {
Str << Opcode << "." << Ty;
}
/// Shared emit routines for common forms of instructions.
static void emitUnaryopGPR(const char *Opcode, const InstMIPS32 *Inst,
const Cfg *Func);
protected:
InstMIPS32(Cfg *Func, InstKindMIPS32 Kind, SizeT Maxsrcs, Variable *Dest)
: InstTarget(Func, static_cast<InstKind>(Kind), Maxsrcs, Dest) {}
......@@ -75,6 +169,166 @@ private:
InstMIPS32Ret(Cfg *Func, Variable *RA, Variable *Source);
};
/// Instructions of the form x := op(y).
template <InstMIPS32::InstKindMIPS32 K>
class InstMIPS32UnaryopGPR : public InstMIPS32 {
InstMIPS32UnaryopGPR() = delete;
InstMIPS32UnaryopGPR(const InstMIPS32UnaryopGPR &) = delete;
InstMIPS32UnaryopGPR &operator=(const InstMIPS32UnaryopGPR &) = delete;
public:
static InstMIPS32UnaryopGPR *create(Cfg *Func, Variable *Dest, Operand *Src) {
return new (Func->allocate<InstMIPS32UnaryopGPR>())
InstMIPS32UnaryopGPR(Func, Dest, Src);
}
void emit(const Cfg *Func) const override {
if (!BuildDefs::dump())
return;
emitUnaryopGPR(Opcode, this, Func);
}
void emitIAS(const Cfg *Func) const override {
(void)Func;
llvm_unreachable("Not yet implemented");
}
void dump(const Cfg *Func) const override {
if (!BuildDefs::dump())
return;
Ostream &Str = Func->getContext()->getStrDump();
dumpOpcode(Str, Opcode, getDest()->getType());
Str << " ";
dumpDest(Func);
Str << ", ";
dumpSources(Func);
}
static bool classof(const Inst *Inst) { return isClassof(Inst, K); }
protected:
InstMIPS32UnaryopGPR(Cfg *Func, Variable *Dest, Operand *Src)
: InstMIPS32(Func, K, 1, Dest) {
addSource(Src);
}
private:
static const char *Opcode;
};
template <InstMIPS32::InstKindMIPS32 K, bool Signed = false>
class InstMIPS32Imm16 : public InstMIPS32 {
InstMIPS32Imm16() = delete;
InstMIPS32Imm16(const InstMIPS32Imm16 &) = delete;
InstMIPS32Imm16 &operator=(const InstMIPS32Imm16 &) = delete;
public:
static InstMIPS32Imm16 *create(Cfg *Func, Variable *Dest, Operand *Source,
uint32_t Imm) {
return new (Func->allocate<InstMIPS32Imm16>())
InstMIPS32Imm16(Func, Dest, Source, Imm);
}
static InstMIPS32Imm16 *create(Cfg *Func, Variable *Dest, uint32_t Imm) {
return new (Func->allocate<InstMIPS32Imm16>())
InstMIPS32Imm16(Func, Dest, Imm);
}
void emit(const Cfg *Func) const override {
if (!BuildDefs::dump())
return;
Ostream &Str = Func->getContext()->getStrEmit();
Str << "\t" << Opcode << "\t";
getDest()->emit(Func);
if (getSrcSize() > 0) {
Str << ", ";
getSrc(0)->emit(Func);
}
Str << ", ";
if (Signed)
Str << (int32_t)Imm;
else
Str << Imm;
Str << "\n";
}
void emitIAS(const Cfg *Func) const override {
(void)Func;
llvm_unreachable("Not yet implemented");
}
void dump(const Cfg *Func) const override {
if (!BuildDefs::dump())
return;
Ostream &Str = Func->getContext()->getStrDump();
Str << " ";
Str << "\t" << Opcode << "\t";
dumpDest(Func);
Str << ", ";
dumpSources(Func);
if (Signed)
Str << (int32_t)Imm;
else
Str << Imm;
Str << "\n";
}
static bool classof(const Inst *Inst) { return isClassof(Inst, K); }
private:
InstMIPS32Imm16(Cfg *Func, Variable *Dest, Operand *Source, uint32_t Imm)
: InstMIPS32(Func, K, 1, Dest), Imm(Imm){
addSource(Source);
}
InstMIPS32Imm16(Cfg *Func, Variable *Dest, uint32_t Imm)
: InstMIPS32(Func, K, 0, Dest), Imm(Imm) {
}
static const char *Opcode;
const uint32_t Imm;
};
typedef InstMIPS32Imm16<InstMIPS32::Addiu, true> InstMIPS32Addiu;
typedef InstMIPS32Imm16<InstMIPS32::Lui> InstMIPS32Lui;
typedef InstMIPS32UnaryopGPR<InstMIPS32::La> InstMIPS32La;
typedef InstMIPS32Imm16<InstMIPS32::Ori> InstMIPS32Ori;
/// Handles (some of) vmov's various formats.
class InstMIPS32Mov final : public InstMIPS32 {
InstMIPS32Mov() = delete;
InstMIPS32Mov(const InstMIPS32Mov &) = delete;
InstMIPS32Mov &operator=(const InstMIPS32Mov &) = delete;
public:
static InstMIPS32Mov *create(Cfg *Func, Variable *Dest, Operand *Src) {
return new (Func->allocate<InstMIPS32Mov>()) InstMIPS32Mov(Func, Dest, Src);
}
bool isRedundantAssign() const override {
return !isMultiDest() && !isMultiSource() &&
checkForRedundantAssign(getDest(), getSrc(0));
}
//bool isSimpleAssign() const override { return true; }
void emit(const Cfg *Func) const override;
void emitIAS(const Cfg *Func) const override;
void dump(const Cfg *Func) const override;
static bool classof(const Inst *Inst) { return isClassof(Inst, Mov); }
bool isMultiDest() const { return DestHi != nullptr; }
bool isMultiSource() const {
assert(getSrcSize() == 1 || getSrcSize() == 2);
return getSrcSize() == 2;
}
Variable *getDestHi() const { return DestHi; }
private:
InstMIPS32Mov(Cfg *Func, Variable *Dest, Operand *Src);
void emitMultiDestSingleSource(const Cfg *Func) const;
void emitSingleDestMultiSource(const Cfg *Func) const;
void emitSingleDestSingleSource(const Cfg *Func) const;
Variable *DestHi = nullptr;
};
} // end of namespace Ice
#endif // SUBZERO_SRC_ICEINSTMIPS32_H
......@@ -27,7 +27,7 @@ namespace RegMIPS32 {
/// used to binary encode register operands in instructions.
enum AllRegisters {
#define X(val, encode, name, scratch, preserved, stackptr, frameptr, isInt, \
isFP) \
isI64Pair, isFP32, isFP64, isVec128, alias_init) \
val,
REGMIPS32_TABLE
#undef X
......@@ -41,7 +41,8 @@ enum AllRegisters {
/// to binary encode register operands in instructions.
enum GPRRegister {
#define X(val, encode, name, scratch, preserved, stackptr, frameptr, isInt, \
isFP) \
isI64Pair, isFP32, isFP64, isVec128, alias_init) \
\
Encoded_##val encode,
REGMIPS32_GPR_TABLE
#undef X
......
......@@ -24,6 +24,7 @@
#include "IceInstMIPS32.h"
#include "IceLiveness.h"
#include "IceOperand.h"
#include "IcePhiLoweringImpl.h"
#include "IceRegistersMIPS32.h"
#include "IceTargetLoweringMIPS32.def"
#include "IceUtils.h"
......@@ -31,21 +32,41 @@
namespace Ice {
using llvm::isInt;
namespace {
// The maximum number of arguments to pass in GPR registers.
constexpr uint32_t MIPS32_MAX_GPR_ARG = 4;
} // end of anonymous namespace
TargetMIPS32::TargetMIPS32(Cfg *Func) : TargetLowering(Func) {
// TODO: Don't initialize IntegerRegisters and friends every time. Instead,
// initialize in some sort of static initializer for the class.
llvm::SmallBitVector IntegerRegisters(RegMIPS32::Reg_NUM);
llvm::SmallBitVector FloatRegisters(RegMIPS32::Reg_NUM);
llvm::SmallBitVector I64PairRegisters(RegMIPS32::Reg_NUM);
llvm::SmallBitVector Float32Registers(RegMIPS32::Reg_NUM);
llvm::SmallBitVector Float64Registers(RegMIPS32::Reg_NUM);
llvm::SmallBitVector VectorRegisters(RegMIPS32::Reg_NUM);
llvm::SmallBitVector InvalidRegisters(RegMIPS32::Reg_NUM);
ScratchRegs.resize(RegMIPS32::Reg_NUM);
#define X(val, encode, name, scratch, preserved, stackptr, frameptr, isInt, \
isFP) \
isI64Pair, isFP32, isFP64, isVec128, alias_init) \
IntegerRegisters[RegMIPS32::val] = isInt; \
FloatRegisters[RegMIPS32::val] = isFP; \
VectorRegisters[RegMIPS32::val] = isFP; \
I64PairRegisters[RegMIPS32::val] = isI64Pair; \
Float32Registers[RegMIPS32::val] = isFP32; \
Float64Registers[RegMIPS32::val] = isFP64; \
VectorRegisters[RegMIPS32::val] = isVec128; \
RegisterAliases[RegMIPS32::val].resize(RegMIPS32::Reg_NUM); \
for (SizeT RegAlias : alias_init) { \
assert(!RegisterAliases[RegMIPS32::val][RegAlias] && \
"Duplicate alias for " #val); \
RegisterAliases[RegMIPS32::val].set(RegAlias); \
} \
RegisterAliases[RegMIPS32::val].resize(RegMIPS32::Reg_NUM); \
RegisterAliases[RegMIPS32::val].set(RegMIPS32::val); \
assert(RegisterAliases[RegMIPS32::val][RegMIPS32::val]); \
ScratchRegs[RegMIPS32::val] = scratch;
REGMIPS32_TABLE;
#undef X
......@@ -55,8 +76,8 @@ TargetMIPS32::TargetMIPS32(Cfg *Func) : TargetLowering(Func) {
TypeToRegisterSet[IceType_i16] = IntegerRegisters;
TypeToRegisterSet[IceType_i32] = IntegerRegisters;
TypeToRegisterSet[IceType_i64] = IntegerRegisters;
TypeToRegisterSet[IceType_f32] = FloatRegisters;
TypeToRegisterSet[IceType_f64] = FloatRegisters;
TypeToRegisterSet[IceType_f32] = Float32Registers;
TypeToRegisterSet[IceType_f64] = Float64Registers;
TypeToRegisterSet[IceType_v4i1] = VectorRegisters;
TypeToRegisterSet[IceType_v8i1] = VectorRegisters;
TypeToRegisterSet[IceType_v16i1] = VectorRegisters;
......@@ -213,7 +234,7 @@ IceString TargetMIPS32::getRegName(SizeT RegNum, Type Ty) const {
(void)Ty;
static const char *RegNames[] = {
#define X(val, encode, name, scratch, preserved, stackptr, frameptr, isInt, \
isFP) \
isI64Pair, isFP32, isFP64, isVec128, alias_init) \
name,
REGMIPS32_TABLE
#undef X
......@@ -248,39 +269,215 @@ void TargetMIPS32::emitJumpTable(const Cfg *Func,
UnimplementedError(Func->getContext()->getFlags());
}
/// Provide a trivial wrapper to legalize() for this common usage.
Variable *TargetMIPS32::legalizeToReg(Operand *From, int32_t RegNum) {
return llvm::cast<Variable>(legalize(From, Legal_Reg, RegNum));
}
/// Legalize undef values to concrete values.
Operand *TargetMIPS32::legalizeUndef(Operand *From, int32_t RegNum) {
(void)RegNum;
Type Ty = From->getType();
if (llvm::isa<ConstantUndef>(From)) {
// Lower undefs to zero. Another option is to lower undefs to an
// uninitialized register; however, using an uninitialized register
// results in less predictable code.
//
// If in the future the implementation is changed to lower undef
// values to uninitialized registers, a FakeDef will be needed:
// Context.insert(InstFakeDef::create(Func, Reg));
// This is in order to ensure that the live range of Reg is not
// overestimated. If the constant being lowered is a 64 bit value,
// then the result should be split and the lo and hi components will
// need to go in uninitialized registers.
if (isVectorType(Ty))
UnimplementedError(Func->getContext()->getFlags());
return Ctx->getConstantZero(Ty);
}
return From;
}
Variable *TargetMIPS32::makeReg(Type Type, int32_t RegNum) {
// There aren't any 64-bit integer registers for Mips32.
assert(Type != IceType_i64);
Variable *Reg = Func->makeVariable(Type);
if (RegNum == Variable::NoRegister)
Reg->setMustHaveReg();
else
Reg->setRegNum(RegNum);
return Reg;
}
void TargetMIPS32::emitVariable(const Variable *Var) const {
if (!BuildDefs::dump())
return;
Ostream &Str = Ctx->getStrEmit();
(void)Var;
(void)Str;
const Type FrameSPTy = IceType_i32;
if (Var->hasReg()) {
Str << '$' << getRegName(Var->getRegNum(), Var->getType());
return;
} else {
int32_t Offset = Var->getStackOffset();
Str << Offset;
Str << "(" << getRegName(getFrameOrStackReg(), FrameSPTy);
Str << ")";
}
UnimplementedError(Func->getContext()->getFlags());
}
void TargetMIPS32::lowerArguments() {
VarList &Args = Func->getArgs();
if (Args.size() > 0)
UnimplementedError(Func->getContext()->getFlags());
// We are only handling integer registers for now. The Mips o32 ABI is
// somewhat complex but will be implemented in its totality through follow
// on patches.
//
unsigned NumGPRRegsUsed = 0;
// For each register argument, replace Arg in the argument list with the
// home register. Then generate an instruction in the prolog to copy the
// home register to the assigned location of Arg.
Context.init(Func->getEntryNode());
Context.setInsertPoint(Context.getCur());
for (SizeT I = 0, E = Args.size(); I < E; ++I) {
Variable *Arg = Args[I];
Type Ty = Arg->getType();
// TODO(rkotler): handle float/vector types.
if (isVectorType(Ty)) {
UnimplementedError(Func->getContext()->getFlags());
continue;
}
if (isFloatingType(Ty)) {
UnimplementedError(Func->getContext()->getFlags());
continue;
}
if (Ty == IceType_i64) {
if (NumGPRRegsUsed >= MIPS32_MAX_GPR_ARG)
continue;
int32_t RegLo = RegMIPS32::Reg_A0 + NumGPRRegsUsed;
int32_t RegHi = RegLo + 1;
++NumGPRRegsUsed;
// Always start i64 registers at an even register, so this may end
// up padding away a register.
if (RegLo % 2 != 0) {
++RegLo;
++NumGPRRegsUsed;
}
// If this leaves us without room to consume another register,
// leave any previously speculatively consumed registers as consumed.
if (NumGPRRegsUsed >= MIPS32_MAX_GPR_ARG)
continue;
// RegHi = RegMIPS32::Reg_A0 + NumGPRRegsUsed;
++NumGPRRegsUsed;
Variable *RegisterArg = Func->makeVariable(Ty);
auto *RegisterArg64On32 = llvm::cast<Variable64On32>(RegisterArg);
if (BuildDefs::dump())
RegisterArg64On32->setName(Func, "home_reg:" + Arg->getName(Func));
RegisterArg64On32->initHiLo(Func);
RegisterArg64On32->setIsArg();
RegisterArg64On32->getLo()->setRegNum(RegLo);
RegisterArg64On32->getHi()->setRegNum(RegHi);
Arg->setIsArg(false);
Args[I] = RegisterArg64On32;
Context.insert(InstAssign::create(Func, Arg, RegisterArg));
continue;
} else {
assert(Ty == IceType_i32);
if (NumGPRRegsUsed >= MIPS32_MAX_GPR_ARG)
continue;
int32_t RegNum = RegMIPS32::Reg_A0 + NumGPRRegsUsed;
++NumGPRRegsUsed;
Variable *RegisterArg = Func->makeVariable(Ty);
if (BuildDefs::dump()) {
RegisterArg->setName(Func, "home_reg:" + Arg->getName(Func));
}
RegisterArg->setRegNum(RegNum);
RegisterArg->setIsArg();
Arg->setIsArg(false);
Args[I] = RegisterArg;
Context.insert(InstAssign::create(Func, Arg, RegisterArg));
}
}
}
Type TargetMIPS32::stackSlotType() { return IceType_i32; }
void TargetMIPS32::addProlog(CfgNode *Node) {
(void)Node;
return;
UnimplementedError(Func->getContext()->getFlags());
}
void TargetMIPS32::addEpilog(CfgNode *Node) {
(void)Node;
return;
UnimplementedError(Func->getContext()->getFlags());
}
Operand *TargetMIPS32::loOperand(Operand *Operand) {
assert(Operand->getType() == IceType_i64);
if (auto *Var64On32 = llvm::dyn_cast<Variable64On32>(Operand))
return Var64On32->getLo();
if (auto *Const = llvm::dyn_cast<ConstantInteger64>(Operand)) {
return Ctx->getConstantInt32(static_cast<uint32_t>(Const->getValue()));
}
if (auto *Mem = llvm::dyn_cast<OperandMIPS32Mem>(Operand)) {
// Conservatively disallow memory operands with side-effects (pre/post
// increment) in case of duplication.
assert(Mem->getAddrMode() == OperandMIPS32Mem::Offset);
return OperandMIPS32Mem::create(Func, IceType_i32, Mem->getBase(),
Mem->getOffset(), Mem->getAddrMode());
}
llvm_unreachable("Unsupported operand type");
return nullptr;
}
Operand *TargetMIPS32::hiOperand(Operand *Operand) {
assert(Operand->getType() == IceType_i64);
if (Operand->getType() != IceType_i64)
return Operand;
if (auto *Var64On32 = llvm::dyn_cast<Variable64On32>(Operand))
return Var64On32->getHi();
if (auto *Const = llvm::dyn_cast<ConstantInteger64>(Operand)) {
return Ctx->getConstantInt32(
static_cast<uint32_t>(Const->getValue() >> 32));
}
if (auto *Mem = llvm::dyn_cast<OperandMIPS32Mem>(Operand)) {
// Conservatively disallow memory operands with side-effects
// in case of duplication.
assert(Mem->getAddrMode() == OperandMIPS32Mem::Offset);
const Type SplitType = IceType_i32;
Variable *Base = Mem->getBase();
ConstantInteger32 *Offset = Mem->getOffset();
assert(!Utils::WouldOverflowAdd(Offset->getValue(), 4));
int32_t NextOffsetVal = Offset->getValue() + 4;
constexpr bool SignExt = false;
if (!OperandMIPS32Mem::canHoldOffset(SplitType, SignExt, NextOffsetVal)) {
// We have to make a temp variable and add 4 to either Base or Offset.
// If we add 4 to Offset, this will convert a non-RegReg addressing
// mode into a RegReg addressing mode. Since NaCl sandboxing disallows
// RegReg addressing modes, prefer adding to base and replacing instead.
// Thus we leave the old offset alone.
Constant *Four = Ctx->getConstantInt32(4);
Variable *NewBase = Func->makeVariable(Base->getType());
lowerArithmetic(InstArithmetic::create(Func, InstArithmetic::Add, NewBase,
Base, Four));
Base = NewBase;
} else {
Offset =
llvm::cast<ConstantInteger32>(Ctx->getConstantInt32(NextOffsetVal));
}
return OperandMIPS32Mem::create(Func, SplitType, Base, Offset,
Mem->getAddrMode());
}
llvm_unreachable("Unsupported operand type");
return nullptr;
}
llvm::SmallBitVector TargetMIPS32::getRegisterSet(RegSetMask Include,
RegSetMask Exclude) const {
llvm::SmallBitVector Registers(RegMIPS32::Reg_NUM);
#define X(val, encode, name, scratch, preserved, stackptr, frameptr, isInt, \
isFP) \
isI64Pair, isFP32, isFP64, isVec128, alias_init) \
if (scratch && (Include & RegSet_CallerSave)) \
Registers[RegMIPS32::val] = true; \
if (preserved && (Include & RegSet_CalleeSave)) \
......@@ -318,70 +515,117 @@ void TargetMIPS32::lowerAlloca(const InstAlloca *Inst) {
}
void TargetMIPS32::lowerArithmetic(const InstArithmetic *Inst) {
switch (Inst->getOp()) {
case InstArithmetic::_num:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Add:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::And:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Or:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Xor:
Variable *Dest = Inst->getDest();
Operand *Src0 = legalizeUndef(Inst->getSrc(0));
Operand *Src1 = legalizeUndef(Inst->getSrc(1));
(void)Src0;
(void)Src1;
if (Dest->getType() == IceType_i64) {
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Sub:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Mul:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Shl:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Lshr:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Ashr:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Udiv:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Sdiv:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Urem:
} else if (isVectorType(Dest->getType())) {
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Srem:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Fadd:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Fsub:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Fmul:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Fdiv:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Frem:
UnimplementedError(Func->getContext()->getFlags());
break;
} else { // Dest->getType() is non-i64 scalar
switch (Inst->getOp()) {
case InstArithmetic::_num:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Add:
UnimplementedError(Func->getContext()->getFlags());
// Variable *T = makeReg(Dest->getType());
// _add(T, Src0, Src1);
// _mov(Dest, T);
return;
case InstArithmetic::And:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Or:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Xor:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Sub:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Mul:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Shl:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Lshr:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Ashr:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Udiv:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Sdiv:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Urem:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Srem:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Fadd:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Fsub:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Fmul:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Fdiv:
UnimplementedError(Func->getContext()->getFlags());
break;
case InstArithmetic::Frem:
UnimplementedError(Func->getContext()->getFlags());
break;
}
}
}
void TargetMIPS32::lowerAssign(const InstAssign *Inst) {
(void)Inst;
UnimplementedError(Func->getContext()->getFlags());
Variable *Dest = Inst->getDest();
Operand *Src0 = Inst->getSrc(0);
assert(Dest->getType() == Src0->getType());
if (Dest->getType() == IceType_i64) {
Src0 = legalizeUndef(Src0);
Operand *Src0Lo = legalize(loOperand(Src0), Legal_Reg);
Operand *Src0Hi = legalize(hiOperand(Src0), Legal_Reg);
Variable *DestLo = llvm::cast<Variable>(loOperand(Dest));
Variable *DestHi = llvm::cast<Variable>(hiOperand(Dest));
// Variable *T_Lo = nullptr, *T_Hi = nullptr;
Variable *T_Lo = makeReg(IceType_i32);
Variable *T_Hi = makeReg(IceType_i32);
_mov(T_Lo, Src0Lo);
_mov(DestLo, T_Lo);
_mov(T_Hi, Src0Hi);
_mov(DestHi, T_Hi);
} else {
Operand *SrcR;
if (Dest->hasReg()) {
// If Dest already has a physical register, then legalize the Src operand
// into a Variable with the same register assignment. This especially
// helps allow the use of Flex operands.
SrcR = legalize(Src0, Legal_Reg, Dest->getRegNum());
} else {
// Dest could be a stack operand. Since we could potentially need
// to do a Store (and store can only have Register operands),
// legalize this to a register.
SrcR = legalize(Src0, Legal_Reg);
}
if (isVectorType(Dest->getType())) {
UnimplementedError(Func->getContext()->getFlags());
} else {
_mov(Dest, SrcR);
}
}
}
void TargetMIPS32::lowerBr(const InstBr *Inst) {
......@@ -608,8 +852,32 @@ void TargetMIPS32::lowerPhi(const InstPhi * /*Inst*/) {
void TargetMIPS32::lowerRet(const InstRet *Inst) {
Variable *Reg = nullptr;
if (Inst->hasRetValue())
UnimplementedError(Func->getContext()->getFlags());
if (Inst->hasRetValue()) {
Operand *Src0 = Inst->getRetValue();
switch (Src0->getType()) {
case IceType_i1:
case IceType_i8:
case IceType_i16:
case IceType_i32: {
// Reg = legalizeToReg(Src0, RegMIPS32::Reg_V0);
Operand *Src0F = legalize(Src0, Legal_Reg);
Reg = makeReg(Src0F->getType(), RegMIPS32::Reg_V0);
_mov(Reg, Src0F);
break;
}
case IceType_i64: {
Src0 = legalizeUndef(Src0);
Variable *R0 = legalizeToReg(loOperand(Src0), RegMIPS32::Reg_V0);
Variable *R1 = legalizeToReg(hiOperand(Src0), RegMIPS32::Reg_V1);
Reg = R0;
Context.insert(InstFakeUse::create(Func, R1));
break;
}
default:
UnimplementedError(Func->getContext()->getFlags());
}
}
_ret(getPhysicalRegister(RegMIPS32::Reg_RA), Reg);
}
......@@ -640,7 +908,7 @@ void TargetMIPS32::lowerUnreachable(const InstUnreachable * /*Inst*/) {
// integrity of liveness analysis. Undef values are also turned into zeroes,
// since loOperand() and hiOperand() don't expect Undef input.
void TargetMIPS32::prelowerPhis() {
UnimplementedError(Func->getContext()->getFlags());
PhiLowering::prelowerPhis32Bit<TargetMIPS32>(this, Context.getNode(), Func);
}
void TargetMIPS32::postLower() {
......@@ -705,7 +973,86 @@ void TargetDataMIPS32::lowerJumpTables() {
UnimplementedError(Ctx->getFlags());
}
// Helper for legalize() to emit the right code to lower an operand to a
// register of the appropriate type.
Variable *TargetMIPS32::copyToReg(Operand *Src, int32_t RegNum) {
Type Ty = Src->getType();
Variable *Reg = makeReg(Ty, RegNum);
if (isVectorType(Ty) || isFloatingType(Ty)) {
UnimplementedError(Ctx->getFlags());
} else {
// Mov's Src operand can really only be the flexible second operand type
// or a register. Users should guarantee that.
_mov(Reg, Src);
}
return Reg;
}
Operand *TargetMIPS32::legalize(Operand *From, LegalMask Allowed,
int32_t RegNum) {
Type Ty = From->getType();
// Assert that a physical register is allowed. To date, all calls
// to legalize() allow a physical register. Legal_Flex converts
// registers to the right type OperandMIPS32FlexReg as needed.
assert(Allowed & Legal_Reg);
// Go through the various types of operands:
// OperandMIPS32Mem, OperandMIPS32Flex, Constant, and Variable.
// Given the above assertion, if type of operand is not legal
// (e.g., OperandMIPS32Mem and !Legal_Mem), we can always copy
// to a register.
if (auto C = llvm::dyn_cast<ConstantRelocatable>(From)) {
(void)C;
return From;
} else if (auto *C32 = llvm::dyn_cast<ConstantInteger32>(From)) {
uint32_t Value = static_cast<uint32_t>(C32->getValue());
// Check if the immediate will fit in a Flexible second operand,
// if a Flexible second operand is allowed. We need to know the exact
// value, so that rules out relocatable constants.
// Also try the inverse and use MVN if possible.
// Do a movw/movt to a register.
Variable *Reg;
if (RegNum == Variable::NoRegister)
Reg = makeReg(Ty, RegNum);
else
Reg = getPhysicalRegister(RegNum);
if (isInt<16>(int32_t(Value))) {
_addiu(Reg, getPhysicalRegister(RegMIPS32::Reg_ZERO, Ty), Value);
} else {
uint32_t UpperBits = (Value >> 16) & 0xFFFF;
(void)UpperBits;
uint32_t LowerBits = Value & 0xFFFF;
Variable *TReg = makeReg(Ty, RegNum);
_lui(TReg, UpperBits);
_ori(Reg, TReg, LowerBits);
}
return Reg;
}
if (auto Var = llvm::dyn_cast<Variable>(From)) {
// Check if the variable is guaranteed a physical register. This
// can happen either when the variable is pre-colored or when it is
// assigned infinite weight.
bool MustHaveRegister = (Var->hasReg() || Var->mustHaveReg());
// We need a new physical register for the operand if:
// Mem is not allowed and Var isn't guaranteed a physical
// register, or
// RegNum is required and Var->getRegNum() doesn't match.
if ((!(Allowed & Legal_Mem) && !MustHaveRegister) ||
(RegNum != Variable::NoRegister && RegNum != Var->getRegNum())) {
From = copyToReg(From, RegNum);
}
return From;
}
return From;
}
TargetHeaderMIPS32::TargetHeaderMIPS32(GlobalContext *Ctx)
: TargetHeaderLowering(Ctx) {}
void TargetHeaderMIPS32::lower() {
OstreamLocker L(Ctx);
Ostream &Str = Ctx->getStrEmit();
Str << "\t.set\tnomicromips\n";
Str << "\t.set\tnomips16\n";
}
} // end of namespace Ice
......@@ -94,10 +94,74 @@ public:
Context.insert(InstMIPS32Ret::create(Func, RA, Src0));
}
void _addiu(Variable *Dest, Variable *Src, uint32_t Imm) {
Context.insert(InstMIPS32Addiu::create(Func, Dest, Src, Imm));
}
void _lui(Variable *Dest, uint32_t Imm) {
Context.insert(InstMIPS32Lui::create(Func, Dest, Imm));
}
void _mov(Variable *Dest, Operand *Src0) {
assert(Dest != nullptr);
// Variable* Src0_ = llvm::dyn_cast<Variable>(Src0);
if (llvm::isa<ConstantRelocatable>(Src0)) {
Context.insert(InstMIPS32La::create(Func, Dest, Src0));
} else {
auto *Instr = InstMIPS32Mov::create(Func, Dest, Src0);
Context.insert(Instr);
if (Instr->isMultiDest()) {
// If Instr is multi-dest, then Dest must be a Variable64On32. We add a
// fake-def for Instr.DestHi here.
assert(llvm::isa<Variable64On32>(Dest));
Context.insert(InstFakeDef::create(Func, Instr->getDestHi()));
}
}
}
void _ori(Variable *Dest, Variable *Src, uint32_t Imm) {
Context.insert(InstMIPS32Ori::create(Func, Dest, Src, Imm));
}
void lowerArguments() override;
/// Operand legalization helpers. To deal with address mode constraints,
/// the helpers will create a new Operand and emit instructions that
/// guarantee that the Operand kind is one of those indicated by the
/// LegalMask (a bitmask of allowed kinds). If the input Operand is known
/// to already meet the constraints, it may be simply returned as the result,
/// without creating any new instructions or operands.
enum OperandLegalization {
Legal_None = 0,
Legal_Reg = 1 << 0, // physical register, not stack location
Legal_Imm = 1 << 1,
Legal_Mem = 1 << 2,
Legal_All = ~Legal_None
};
typedef uint32_t LegalMask;
Operand *legalize(Operand *From, LegalMask Allowed = Legal_All,
int32_t RegNum = Variable::NoRegister);
Variable *legalizeToVar(Operand *From, int32_t RegNum = Variable::NoRegister);
Variable *legalizeToReg(Operand *From, int32_t RegNum = Variable::NoRegister);
Variable *makeReg(Type Ty, int32_t RegNum = Variable::NoRegister);
static Type stackSlotType();
Variable *copyToReg(Operand *Src, int32_t RegNum = Variable::NoRegister);
void addProlog(CfgNode *Node) override;
void addEpilog(CfgNode *Node) override;
// Ensure that a 64-bit Variable has been split into 2 32-bit
// Variables, creating them if necessary. This is needed for all
// I64 operations.
void split64(Variable *Var);
Operand *loOperand(Operand *Operand);
Operand *hiOperand(Operand *Operand);
Operand *legalizeUndef(Operand *From, int32_t RegNum = Variable::NoRegister);
protected:
explicit TargetMIPS32(Cfg *Func);
......@@ -131,8 +195,6 @@ protected:
const llvm::SmallBitVector &ExcludeRegisters,
uint64_t Salt) const override;
static Type stackSlotType();
bool UsesFramePointer = false;
bool NeedsStackAlignment = false;
llvm::SmallBitVector TypeToRegisterSet[IceType_NUM];
......@@ -178,6 +240,8 @@ public:
return std::unique_ptr<TargetHeaderLowering>(new TargetHeaderMIPS32(Ctx));
}
void lower() override;
protected:
explicit TargetHeaderMIPS32(GlobalContext *Ctx);
......
......@@ -15,6 +15,16 @@
; RUN: | %if --need=target_ARM32 --need=allow_dump \
; RUN: --command FileCheck --check-prefix ARM32 %s
; TODO(RKotler): Stop skipping unimplemented parts (via --skip-unimplemented)
; once enough infrastructure is in. Also, switch to --filetype=obj
; when possible.
; RUN: %if --need=target_MIPS32 --need=allow_dump \
; RUN: --command %p2i --filetype=asm --assemble \
; RUN: --disassemble --target mips32 -i %s --args -O2 --skip-unimplemented \
; RUN: -allow-externally-defined-symbols \
; RUN: | %if --need=target_MIPS32 --need=allow_dump \
; RUN: --command FileCheck --check-prefix MIPS32 %s
; For x86-32, integer arguments use the stack.
; For ARM32, integer arguments can be r0-r3. i64 arguments occupy two
; adjacent 32-bit registers, and require the first to be an even register.
......@@ -31,6 +41,9 @@ entry:
; CHECK-NEXT: ret
; ARM32-LABEL: test_returning32_arg0
; ARM32-NEXT: bx lr
; MIPS32-LABEL: test_returning32_arg0
; MIPS32: move v0,a0
; MIPS32-NEXT: jr ra
define internal i32 @test_returning32_arg1(i32 %arg0, i32 %arg1, i32 %arg2, i32 %arg3, i32 %arg4, i32 %arg5, i32 %arg6, i32 %arg7) {
entry:
......@@ -42,7 +55,9 @@ entry:
; ARM32-LABEL: test_returning32_arg1
; ARM32-NEXT: mov r0, r1
; ARM32-NEXT: bx lr
; MIPS32-LABEL: test_returning32_arg1
; MIPS32: move v0,a1
; MIPS32-NEXT: jr ra
define internal i32 @test_returning32_arg2(i32 %arg0, i32 %arg1, i32 %arg2, i32 %arg3, i32 %arg4, i32 %arg5, i32 %arg6, i32 %arg7) {
entry:
......@@ -54,6 +69,9 @@ entry:
; ARM32-LABEL: test_returning32_arg2
; ARM32-NEXT: mov r0, r2
; ARM32-NEXT: bx lr
; MIPS32-LABEL: test_returning32_arg2
; MIPS32: move v0,a2
; MIPS32-NEXT: jr ra
define internal i32 @test_returning32_arg3(i32 %arg0, i32 %arg1, i32 %arg2, i32 %arg3, i32 %arg4, i32 %arg5, i32 %arg6, i32 %arg7) {
......@@ -103,6 +121,10 @@ entry:
; CHECK: ret
; ARM32-LABEL: test_returning64_arg0
; ARM32-NEXT: bx lr
; MIPS32-LABEL: test_returning64_arg0
; MIPS32-NEXT: move v0,a0
; MIPS32-NEXT: move v1,a1
define internal i64 @test_returning64_arg1(i64 %arg0, i64 %arg1, i64 %arg2, i64 %arg3) {
entry:
......@@ -116,6 +138,9 @@ entry:
; ARM32-NEXT: mov r0, r2
; ARM32-NEXT: mov r1, r3
; ARM32-NEXT: bx lr
; MIPS32-LABEL: test_returning64_arg1
; MIPS32-NEXT: move v0,a2
; MIPS32-NEXT: move v1,a3
define internal i64 @test_returning64_arg2(i64 %arg0, i64 %arg1, i64 %arg2, i64 %arg3) {
entry:
......
......@@ -14,6 +14,15 @@
; RUN: | %if --need=target_ARM32 --need=allow_dump \
; RUN: --command FileCheck --check-prefix ARM32 %s
; TODO(rkotler): Stop skipping unimplemented parts (via --skip-unimplemented)
; once enough infrastructure is in. Also, switch to --filetype=obj
; when possible.
; RUN: %if --need=target_MIPS32 --need=allow_dump \
; RUN: --command %p2i --filetype=asm --assemble \
; RUN: --disassemble --target mips32 -i %s --args -O2 --skip-unimplemented \
; RUN: | %if --need=target_MIPS32 --need=allow_dump \
; RUN: --command FileCheck --check-prefix MIPS32 %s
; Test 8-bits of all ones rotated right by various amounts (even vs odd).
; ARM has a shifter that allows encoding 8-bits rotated right by even amounts.
; The first few "rotate right" test cases are expressed as shift-left.
......@@ -25,6 +34,8 @@ define internal i32 @ret_8bits_shift_left0() {
; CHECK-NEXT: mov eax,0xff
; ARM32-LABEL: ret_8bits_shift_left0
; ARM32-NEXT: mov r0, #255
; MIPS32-LABEL: ret_8bits_shift_left0
; MIPS32-NEXT: li v0,255
define internal i32 @ret_8bits_shift_left1() {
ret i32 510
......@@ -33,6 +44,8 @@ define internal i32 @ret_8bits_shift_left1() {
; CHECK-NEXT: mov eax,0x1fe
; ARM32-LABEL: ret_8bits_shift_left1
; ARM32-NEXT: movw r0, #510
; MIPS32-LABEL: ret_8bits_shift_left1
; MIPS32-NEXT: li v0,510
define internal i32 @ret_8bits_shift_left2() {
ret i32 1020
......@@ -41,6 +54,8 @@ define internal i32 @ret_8bits_shift_left2() {
; CHECK-NEXT: mov eax,0x3fc
; ARM32-LABEL: ret_8bits_shift_left2
; ARM32-NEXT: mov r0, #1020
; MIPS32-LABEL: ret_8bits_shift_left2
; MIPS32-NEXT: li v0,1020
define internal i32 @ret_8bits_shift_left4() {
ret i32 4080
......@@ -49,6 +64,8 @@ define internal i32 @ret_8bits_shift_left4() {
; CHECK-NEXT: mov eax,0xff0
; ARM32-LABEL: ret_8bits_shift_left4
; ARM32-NEXT: mov r0, #4080
; MIPS32-LABEL: ret_8bits_shift_left4
; MIPS32-NEXT: li v0,4080
define internal i32 @ret_8bits_shift_left14() {
ret i32 4177920
......@@ -57,6 +74,9 @@ define internal i32 @ret_8bits_shift_left14() {
; CHECK-NEXT: mov eax,0x3fc000
; ARM32-LABEL: ret_8bits_shift_left14
; ARM32-NEXT: mov r0, #4177920
; MIPS32-LABEL: ret_8bits_shift_left14
; MIPS32-NEXT: lui v0,0x3f
; MIPS32-NEXT: ori v0,v0,0xc000
define internal i32 @ret_8bits_shift_left15() {
ret i32 8355840
......@@ -66,6 +86,9 @@ define internal i32 @ret_8bits_shift_left15() {
; ARM32-LABEL: ret_8bits_shift_left15
; ARM32-NEXT: movw r0, #32768
; ARM32-NEXT: movt r0, #127
; MIPS32-LABEL: ret_8bits_shift_left15
; MIPS32-NEXT: lui v0,0x7f
; MIPS32-NEXT: ori v0,v0,0x8000
; Shift 8 bits left by 24 to the i32 limit. This is also ror by 8 bits.
......@@ -77,6 +100,9 @@ define internal i32 @ret_8bits_shift_left24() {
; ARM32-LABEL: ret_8bits_shift_left24
; ARM32-NEXT: mov r0, #-16777216
; ARM32-NEXT: bx lr
; MIPS32-LABEL: ret_8bits_shift_left24
; MIPS32-NEXT: lui v0,0xff00
; MIPS32-NEXT: ori v0,v0,0x0
; The next few cases wrap around and actually demonstrate the rotation.
......@@ -88,6 +114,9 @@ define internal i32 @ret_8bits_ror7() {
; ARM32-LABEL: ret_8bits_ror7
; ARM32-NEXT: movw r0, #1
; ARM32-NEXT: movt r0, #65024
; MIPS32-LABEL: ret_8bits_ror7
; MIPS32-NEXT: lui v0,0xfe00
; MIPS32-NEXT: ori v0,v0,0x1
define internal i32 @ret_8bits_ror6() {
ret i32 4227858435
......@@ -97,6 +126,9 @@ define internal i32 @ret_8bits_ror6() {
; ARM32-LABEL: ret_8bits_ror6
; ARM32-NEXT: mov r0, #-67108861
; ARM32-NEXT: bx lr
; MIPS32-LABEL: ret_8bits_ror6
; MIPS32-NEXT: lui v0,0xfc00
; MIPS32-NEXT: ori v0,v0,0x3
define internal i32 @ret_8bits_ror5() {
ret i32 4160749575
......@@ -106,6 +138,9 @@ define internal i32 @ret_8bits_ror5() {
; ARM32-LABEL: ret_8bits_ror5
; ARM32-NEXT: movw r0, #7
; ARM32-NEXT: movt r0, #63488
; MIPS32-LABEL: ret_8bits_ror5
; MIPS32-NEXT: lui v0,0xf800
; MIPS32-NEXT: ori v0,v0,0x7
define internal i32 @ret_8bits_ror4() {
ret i32 4026531855
......@@ -115,6 +150,9 @@ define internal i32 @ret_8bits_ror4() {
; ARM32-LABEL: ret_8bits_ror4
; ARM32-NEXT: mov r0, #-268435441
; ARM32-NEXT: bx lr
; MIPS32-LABEL: ret_8bits_ror4
; MIPS32-NEXT: lui v0,0xf000
; MIPS32-NEXT: ori v0,v0,0xf
define internal i32 @ret_8bits_ror3() {
ret i32 3758096415
......@@ -124,6 +162,10 @@ define internal i32 @ret_8bits_ror3() {
; ARM32-LABEL: ret_8bits_ror3
; ARM32-NEXT: movw r0, #31
; ARM32-NEXT: movt r0, #57344
; MIPS32-LABEL: ret_8bits_ror3
; MIPS32-NEXT: lui v0,0xe000
; MIPS32-NEXT: ori v0,v0,0x1f
define internal i32 @ret_8bits_ror2() {
ret i32 3221225535
......@@ -133,6 +175,9 @@ define internal i32 @ret_8bits_ror2() {
; ARM32-LABEL: ret_8bits_ror2
; ARM32-NEXT: mov r0, #-1073741761
; ARM32-NEXT: bx lr
; MIPS32-LABEL: ret_8bits_ror2
; MIPS32-NEXT: lui v0,0xc000
; MIPS32-NEXT: ori v0,v0,0x3f
define internal i32 @ret_8bits_ror1() {
ret i32 2147483775
......@@ -142,6 +187,9 @@ define internal i32 @ret_8bits_ror1() {
; ARM32-LABEL: ret_8bits_ror1
; ARM32-NEXT: movw r0, #127
; ARM32-NEXT: movt r0, #32768
; MIPS32-LABEL: ret_8bits_ror1
; MIPS32-NEXT: lui v0,0x8000
; MIPS32-NEXT: ori v0,v0,0x7f
; Some architectures can handle 16-bits at a time efficiently,
; so also test those.
......@@ -154,6 +202,9 @@ define internal i32 @ret_16bits_lower() {
; ARM32-LABEL: ret_16bits_lower
; ARM32-NEXT: movw r0, #65535
; ARM32-NEXT: bx lr
; MIPS32-LABEL: ret_16bits_lower
; MIPS32-NEXT: lui v0,0x0
; MIPS32-NEXT: ori v0,v0,0xffff
define internal i32 @ret_17bits_lower() {
ret i32 131071
......@@ -163,6 +214,10 @@ define internal i32 @ret_17bits_lower() {
; ARM32-LABEL: ret_17bits_lower
; ARM32-NEXT: movw r0, #65535
; ARM32-NEXT: movt r0, #1
; MIPS32-LABEL: ret_17bits_lower
; MIPS32-NEXT: lui v0,0x1
; MIPS32-NEXT: ori v0,v0,0xffff
define internal i32 @ret_16bits_upper() {
ret i32 4294901760
......@@ -172,6 +227,10 @@ define internal i32 @ret_16bits_upper() {
; ARM32-LABEL: ret_16bits_upper
; ARM32-NEXT: movw r0, #0
; ARM32-NEXT: movt r0, #65535
; MIPS32-LABEL: ret_16bits_upper
; MIPS32-NEXT: lui v0,0xffff
; MIPS32-NEXT: ori v0,v0,0x0
; Some 32-bit immediates can be inverted, and moved in a single instruction.
......@@ -183,6 +242,8 @@ define internal i32 @ret_8bits_inverted_shift_left0() {
; ARM32-LABEL: ret_8bits_inverted_shift_left0
; ARM32-NEXT: mvn r0, #255
; ARM32-NEXT: bx lr
; MIPS32-LABEL: ret_8bits_inverted_shift_left0
; MIPS32-NEXT: li v0,-256
define internal i32 @ret_8bits_inverted_shift_left24() {
ret i32 16777215
......@@ -192,6 +253,9 @@ define internal i32 @ret_8bits_inverted_shift_left24() {
; ARM32-LABEL: ret_8bits_inverted_shift_left24
; ARM32-NEXT: mvn r0, #-16777216
; ARM32-NEXT: bx lr
; MIPS32-LABEL: ret_8bits_inverted_shift_left24
; MIPS32-NEXT: lui v0,0xff
; MIPS32-NEXT: ori v0,v0,0xffff
define internal i32 @ret_8bits_inverted_ror2() {
ret i32 1073741760
......@@ -201,6 +265,9 @@ define internal i32 @ret_8bits_inverted_ror2() {
; ARM32-LABEL: ret_8bits_inverted_ror2
; ARM32-NEXT: mvn r0, #-1073741761
; ARM32-NEXT: bx lr
; MIPS32-LABEL: ret_8bits_inverted_ror2
; MIPS32-NEXT: lui v0,0x3fff
; MIPS32-NEXT: ori v0,v0,0xffc0
define internal i32 @ret_8bits_inverted_ror6() {
ret i32 67108860
......@@ -210,6 +277,10 @@ define internal i32 @ret_8bits_inverted_ror6() {
; ARM32-LABEL: ret_8bits_inverted_ror6
; ARM32-NEXT: mvn r0, #-67108861
; ARM32-NEXT: bx lr
; MIPS32-LABEL: ret_8bits_inverted_ror6
; MIPS32-NEXT: lui v0,0x3ff
; MIPS32-NEXT: ori v0,v0,0xfffc
define internal i32 @ret_8bits_inverted_ror7() {
ret i32 33554430
......@@ -219,6 +290,9 @@ define internal i32 @ret_8bits_inverted_ror7() {
; ARM32-LABEL: ret_8bits_inverted_ror7
; ARM32-NEXT: movw r0, #65534
; ARM32-NEXT: movt r0, #511
; MIPS32-LABEL: ret_8bits_inverted_ror7
; MIPS32-NEXT: lui v0,0x1ff
; MIPS32-NEXT: ori v0,v0,0xfffe
; 64-bit immediates.
......@@ -231,6 +305,10 @@ define internal i64 @ret_64bits_shift_left0() {
; ARM32-LABEL: ret_64bits_shift_left0
; ARM32-NEXT: movw r0, #255
; ARM32-NEXT: movw r1, #255
; MIPS32-LABEL: ret_64bits_shift_left0
; MIPS32-NEXT: li v0,255
; MIPS32-NEXT: li v1,255
; A relocatable constant is assumed to require 32-bits along with
; relocation directives.
......@@ -246,3 +324,8 @@ define internal i32 @ret_addr() {
; ARM32-LABEL: ret_addr
; ARM32-NEXT: movw r0, #0 {{.*}} R_ARM_MOVW_ABS_NC _start
; ARM32-NEXT: movt r0, #0 {{.*}} R_ARM_MOVT_ABS _start
; TODO(RKotler) emitting proper li but in disassembly
; it shows up only in the relocation records. Should emit
; without the macro but we still need to add GOT implementation
; to finish this case
;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment