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
......@@ -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
......
......@@ -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