Commit 1d0690bc by Jaydeep Patil Committed by Jim Stichnoth

[SubZero] Implement load and store for MIPS

This patch implements lowerLoad and extends existing lowerStore for byte, short and floating-point types. The patch also modifies PostLoweringLegalizer for conversion of mov to load or store. R=stichnot@chromium.org Review URL: https://codereview.chromium.org/2301303003 . Patch from Jaydeep Patil <jaydeep.patil@imgtec.com>.
parent a0b720de
......@@ -649,10 +649,8 @@ void InstMIPS32Mov::dump(const Cfg *Func) const {
Str << ", ";
DestHi->dump(Func);
}
dumpOpcode(Str, " = mov", getDest()->getType());
Str << " ";
dumpSources(Func);
}
......@@ -711,9 +709,7 @@ void InstMIPS32Mov::emitSingleDestSingleSource(const Cfg *Func) const {
const char *ActualOpcode = nullptr;
const bool DestIsReg = Dest->hasReg();
const bool DestIsMem = !Dest->hasReg();
const bool SrcIsReg = (SrcV && SrcV->hasReg());
const bool SrcIsMem = !(SrcV && SrcV->hasReg());
// reg to reg
if (DestIsReg && SrcIsReg) {
......@@ -729,8 +725,8 @@ void InstMIPS32Mov::emitSingleDestSingleSource(const Cfg *Func) const {
case IceType_i16:
case IceType_i32:
Str << "\t"
"move"
"\t";
<< "move"
<< "\t";
getDest()->emit(Func);
Str << ", ";
getSrc(0)->emit(Func);
......@@ -748,64 +744,7 @@ void InstMIPS32Mov::emitSingleDestSingleSource(const Cfg *Func) const {
return;
}
// reg to stack
if (DestIsMem && SrcIsReg) {
switch (Dest->getType()) {
case IceType_f32:
ActualOpcode = "swc1";
break;
case IceType_f64:
ActualOpcode = "sdc1";
break;
case IceType_i1:
case IceType_i8:
case IceType_i16:
case IceType_i32:
ActualOpcode = "sw";
break;
default:
UnimplementedError(getFlags());
return;
}
assert(ActualOpcode);
Str << "\t" << ActualOpcode << "\t";
getSrc(0)->emit(Func);
Str << ", ";
getDest()->emit(Func);
return;
}
// stack to reg
if (DestIsReg && SrcIsMem) {
switch (Dest->getType()) {
case IceType_f32:
ActualOpcode = "lwc1";
break;
case IceType_f64:
ActualOpcode = "ldc1";
break;
case IceType_i1:
case IceType_i8:
case IceType_i16:
case IceType_i32:
ActualOpcode = "lw";
break;
default:
UnimplementedError(getFlags());
return;
}
assert(ActualOpcode);
Str << "\t" << ActualOpcode << "\t";
getDest()->emit(Func);
Str << ", ";
getSrc(0)->emit(Func);
return;
}
// stack to stack
llvm::report_fatal_error("mov cant copy stack to stack.");
llvm::report_fatal_error("Invalid mov instruction. Dest or Src is memory.");
}
template <> void InstMIPS32Addiu::emitIAS(const Cfg *Func) const {
......
......@@ -110,8 +110,21 @@ public:
static bool canHoldOffset(Type Ty, bool SignExt, int32_t Offset);
void dump(const Cfg *Func, Ostream &Str) const override {
(void)Func;
(void)Str;
if (!BuildDefs::dump())
return;
Str << "[";
if (Func)
getBase()->dump(Func);
else
getBase()->dump(Str);
Str << ", ";
getOffset()->dump(Func, Str);
Str << "] AddrMode==";
if (getAddrMode() == Offset) {
Str << "Offset";
} else {
Str << "Unknown";
}
}
private:
......@@ -515,8 +528,37 @@ public:
if (!BuildDefs::dump())
return;
Ostream &Str = Func->getContext()->getStrEmit();
assert(getSrcSize() == 1);
Str << "\t" << Opcode << "\t";
const Type Ty = getDest()->getType();
switch (Ty) {
case IceType_i1:
case IceType_i8:
Str << "\t"
<< "lb"
<< "\t";
break;
case IceType_i16:
Str << "\t"
<< "lh"
<< "\t";
break;
case IceType_i32:
Str << "\t"
<< "lw"
<< "\t";
break;
case IceType_f32:
Str << "\t"
<< "lwc1"
<< "\t";
break;
case IceType_f64:
Str << "\t"
<< "ldc1"
<< "\t";
break;
default:
llvm_unreachable("InstMIPS32Load unknown type");
}
getDest()->emit(Func);
Str << ", ";
emitRelocOp(Str, Reloc);
......@@ -532,13 +574,12 @@ public:
if (!BuildDefs::dump())
return;
Ostream &Str = Func->getContext()->getStrDump();
Str << "\t" << Opcode << "\t";
dumpOpcode(Str, Opcode, getDest()->getType());
Str << " ";
getDest()->dump(Func);
Str << ", ";
emitRelocOp(Str, Reloc);
Str << (Reloc ? "(" : "");
getSrc(0)->dump(Func);
Str << (Reloc ? ")" : "");
}
static bool classof(const Inst *Inst) { return isClassof(Inst, K); }
......@@ -572,7 +613,37 @@ public:
return;
Ostream &Str = Func->getContext()->getStrEmit();
assert(getSrcSize() == 2);
Str << "\t" << Opcode << "\t";
const Type Ty = getSrc(0)->getType();
switch (Ty) {
case IceType_i1:
case IceType_i8:
Str << "\t"
<< "sb"
<< "\t";
break;
case IceType_i16:
Str << "\t"
<< "sh"
<< "\t";
break;
case IceType_i32:
Str << "\t"
<< "sw"
<< "\t";
break;
case IceType_f32:
Str << "\t"
<< "swc1"
<< "\t";
break;
case IceType_f64:
Str << "\t"
<< "sdc1"
<< "\t";
break;
default:
llvm_unreachable("InstMIPS32Store unknown type");
}
getSrc(0)->emit(Func);
Str << ", ";
emitRelocOp(Str, Reloc);
......@@ -581,20 +652,19 @@ public:
void emitIAS(const Cfg *Func) const override {
(void)Func;
llvm_unreachable("Not yet implemented");
llvm_unreachable("InstMIPS32Store: Not yet implemented");
}
void dump(const Cfg *Func) const override {
if (!BuildDefs::dump())
return;
Ostream &Str = Func->getContext()->getStrDump();
Str << "\t" << Opcode << "\t";
dumpOpcode(Str, Opcode, getSrc(0)->getType());
Str << " ";
getSrc(0)->dump(Func);
Str << ", ";
emitRelocOp(Str, Reloc);
Str << (Reloc ? "(" : "");
getSrc(1)->dump(Func);
Str << (Reloc ? ")" : "");
}
static bool classof(const Inst *Inst) { return isClassof(Inst, K); }
......@@ -775,11 +845,12 @@ public:
if (!BuildDefs::dump())
return;
Ostream &Str = Func->getContext()->getStrDump();
dumpOpcode(Str, Opcode, getDest()->getType());
Str << " ";
Str << "\t" << Opcode << "\t";
dumpDest(Func);
Str << ", ";
dumpSources(Func);
Str << ", ";
if (Signed)
Str << (int32_t)Imm;
else
......
......@@ -389,6 +389,11 @@ void TargetMIPS32::translateOm1() {
return;
Func->dump("After stack frame mapping");
postLowerLegalization();
if (Func->hasError())
return;
Func->dump("After postLowerLegalization");
// Nop insertion
if (getFlags().getShouldDoNopInsertion()) {
Func->doNopInsertion();
......@@ -496,13 +501,14 @@ OperandMIPS32Mem *TargetMIPS32::formMemoryOperand(Operand *Operand, Type Ty) {
// OperandMIPS32Mem, so in that case it wouldn't need another level of
// transformation.
if (auto *Mem = llvm::dyn_cast<OperandMIPS32Mem>(Operand)) {
return Mem;
return llvm::cast<OperandMIPS32Mem>(legalize(Mem));
}
// If we didn't do address mode optimization, then we only have a base/offset
// to work with. MIPS always requires a base register, so just use that to
// hold the operand.
auto *Base = llvm::cast<Variable>(legalize(Operand, Legal_Reg));
auto *Base = llvm::cast<Variable>(
legalize(Operand, Legal_Reg | Legal_Rematerializable));
const int32_t Offset = Base->hasStackOffset() ? Base->getStackOffset() : 0;
return OperandMIPS32Mem::create(
Func, Ty, Base,
......@@ -516,12 +522,17 @@ void TargetMIPS32::emitVariable(const Variable *Var) const {
const Type FrameSPTy = IceType_i32;
if (Var->hasReg()) {
Str << '$' << getRegName(Var->getRegNum(), Var->getType());
} else {
int32_t Offset = Var->getStackOffset();
Str << Offset;
Str << "($" << getRegName(getFrameOrStackReg(), FrameSPTy);
Str << ")";
return;
}
if (Var->mustHaveReg()) {
llvm::report_fatal_error("Infinite-weight Variable (" + Var->getName() +
") has no register assigned - function " +
Func->getFunctionName());
}
const int32_t Offset = Var->getStackOffset();
Str << Offset;
Str << "($" << getRegName(getFrameOrStackReg(), FrameSPTy);
Str << ")";
}
TargetMIPS32::CallingConv::CallingConv()
......@@ -1041,8 +1052,8 @@ Variable *TargetMIPS32::PostLoweringLegalizer::newBaseRegister(
const bool ShouldSub = Offset != 0 && (-Offset & 0xFFFF0000) == 0;
Variable *ScratchReg = Target->makeReg(IceType_i32, ScratchRegNum);
if (ShouldSub) {
Variable *OffsetVal =
Target->legalizeToReg(Target->Ctx->getConstantInt32(-Offset));
Variable *OffsetVal = Target->legalizeToReg(
Target->Ctx->getConstantInt32(-Offset), ScratchRegNum);
Target->_sub(ScratchReg, Base, OffsetVal);
} else {
Target->_addiu(ScratchReg, Base, Offset);
......@@ -1055,7 +1066,6 @@ void TargetMIPS32::PostLoweringLegalizer::legalizeMov(InstMIPS32Mov *MovInstr) {
Variable *Dest = MovInstr->getDest();
assert(Dest != nullptr);
const Type DestTy = Dest->getType();
(void)DestTy;
assert(DestTy != IceType_i64);
Operand *Src = MovInstr->getSrc(0);
......@@ -1067,30 +1077,80 @@ void TargetMIPS32::PostLoweringLegalizer::legalizeMov(InstMIPS32Mov *MovInstr) {
return;
bool Legalized = false;
if (Dest->hasReg()) {
if (auto *Var = llvm::dyn_cast<Variable>(Src)) {
if (Var->isRematerializable()) {
// This is equivalent to an x86 _lea(RematOffset(%esp/%ebp), Variable).
// ExtraOffset is only needed for frame-pointer based frames as we have
// to account for spill storage.
const int32_t ExtraOffset = (Var->getRegNum() == Target->getFrameReg())
? Target->getFrameFixedAllocaOffset()
: 0;
const int32_t Offset = Var->getStackOffset() + ExtraOffset;
Variable *Base = Target->getPhysicalRegister(Var->getRegNum());
Variable *T = newBaseRegister(Base, Offset, Dest->getRegNum());
Target->_mov(Dest, T);
if (!Dest->hasReg()) {
auto *SrcR = llvm::cast<Variable>(Src);
assert(SrcR->hasReg());
assert(!SrcR->isRematerializable());
const int32_t Offset = Dest->getStackOffset();
// This is a _mov(Mem(), Variable), i.e., a store.
auto *Base = Target->getPhysicalRegister(Target->getFrameOrStackReg());
OperandMIPS32Mem *Addr = OperandMIPS32Mem::create(
Target->Func, DestTy, Base,
llvm::cast<ConstantInteger32>(Target->Ctx->getConstantInt32(Offset)));
// FP arguments are passed in GP reg if first argument is in GP. In this
// case type of the SrcR is still FP thus we need to explicitly generate sw
// instead of swc1.
const RegNumT RegNum = SrcR->getRegNum();
const bool isSrcGPReg = ((unsigned)RegNum >= RegMIPS32::Reg_A0 &&
(unsigned)RegNum <= RegMIPS32::Reg_A3) ||
((unsigned)RegNum >= RegMIPS32::Reg_A0A1 &&
(unsigned)RegNum <= RegMIPS32::Reg_A2A3);
if (SrcTy == IceType_f32 && isSrcGPReg == true) {
Variable *SrcGPR = Target->makeReg(IceType_i32, RegNum);
Target->_sw(SrcGPR, Addr);
} else if (SrcTy == IceType_f64 && isSrcGPReg == true) {
Variable *SrcGPRHi, *SrcGPRLo;
if (RegNum == RegMIPS32::Reg_A0A1) {
SrcGPRLo = Target->makeReg(IceType_i32, RegMIPS32::Reg_A0);
SrcGPRHi = Target->makeReg(IceType_i32, RegMIPS32::Reg_A1);
} else {
SrcGPRLo = Target->makeReg(IceType_i32, RegMIPS32::Reg_A2);
SrcGPRHi = Target->makeReg(IceType_i32, RegMIPS32::Reg_A3);
}
OperandMIPS32Mem *AddrHi = OperandMIPS32Mem::create(
Target->Func, DestTy, Base,
llvm::cast<ConstantInteger32>(
Target->Ctx->getConstantInt32(Offset + 4)));
Target->_sw(SrcGPRLo, Addr);
Target->_sw(SrcGPRHi, AddrHi);
} else {
Target->_sw(SrcR, Addr);
}
Target->Context.insert<InstFakeDef>(Dest);
Legalized = true;
} else if (auto *Var = llvm::dyn_cast<Variable>(Src)) {
if (Var->isRematerializable()) {
// This is equivalent to an x86 _lea(RematOffset(%esp/%ebp), Variable).
// ExtraOffset is only needed for frame-pointer based frames as we have
// to account for spill storage.
const int32_t ExtraOffset = (Var->getRegNum() == Target->getFrameReg())
? Target->getFrameFixedAllocaOffset()
: 0;
const int32_t Offset = Var->getStackOffset() + ExtraOffset;
Variable *Base = Target->getPhysicalRegister(Var->getRegNum());
Variable *T = newBaseRegister(Base, Offset, Dest->getRegNum());
Target->_mov(Dest, T);
Legalized = true;
} else {
if (!Var->hasReg()) {
// This is a _mov(Variable, Mem()), i.e., a load.
const int32_t Offset = Var->getStackOffset();
auto *Base = Target->getPhysicalRegister(Target->getFrameOrStackReg());
OperandMIPS32Mem *Addr;
Addr = OperandMIPS32Mem::create(
Target->Func, DestTy, Base,
llvm::cast<ConstantInteger32>(
Target->Ctx->getConstantInt32(Offset)));
Target->_lw(Dest, Addr);
Legalized = true;
} else if (!Var->hasReg()) {
UnimplementedError(getFlags());
return;
}
}
} else {
UnimplementedError(getFlags());
return;
}
if (Legalized) {
......@@ -1562,7 +1622,6 @@ void TargetMIPS32::lowerAssign(const InstAssign *Instr) {
Operand *Src0Hi = legalize(hiOperand(Src0), Legal_Reg);
auto *DestLo = llvm::cast<Variable>(loOperand(Dest));
auto *DestHi = llvm::cast<Variable>(hiOperand(Dest));
// Variable *T_Lo = nullptr, *T_Hi = nullptr;
auto *T_Lo = I32Reg(), *T_Hi = I32Reg();
_mov(T_Lo, Src0Lo);
_mov(DestLo, T_Lo);
......@@ -2242,7 +2301,13 @@ void TargetMIPS32::lowerIntrinsicCall(const InstIntrinsicCall *Instr) {
}
void TargetMIPS32::lowerLoad(const InstLoad *Instr) {
UnimplementedLoweringError(this, Instr);
// A Load instruction can be treated the same as an Assign instruction, after
// the source operand is transformed into an OperandARM32Mem operand.
Type Ty = Instr->getDest()->getType();
Operand *Src0 = formMemoryOperand(Instr->getSourceAddress(), Ty);
Variable *DestLoad = Instr->getDest();
auto *Assign = InstAssign::create(Func, DestLoad, Src0);
lowerAssign(Assign);
}
void TargetMIPS32::doAddressOptLoad() { UnimplementedError(getFlags()); }
......@@ -2436,9 +2501,11 @@ Variable *TargetMIPS32::copyToReg(Operand *Src, RegNumT RegNum) {
if (isVectorType(Ty)) {
UnimplementedError(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);
if (auto *Mem = llvm::dyn_cast<OperandMIPS32Mem>(Src)) {
_lw(Reg, Mem);
} else {
_mov(Reg, Src);
}
}
return Reg;
}
......@@ -2450,11 +2517,59 @@ Operand *TargetMIPS32::legalize(Operand *From, LegalMask Allowed,
// to legalize() allow a physical register. Legal_Flex converts
// registers to the right type OperandMIPS32FlexReg as needed.
assert(Allowed & Legal_Reg);
if (RegNum.hasNoValue()) {
if (Variable *Subst = getContext().availabilityGet(From)) {
// At this point we know there is a potential substitution available.
if (!Subst->isRematerializable() && Subst->mustHaveReg() &&
!Subst->hasReg()) {
// At this point we know the substitution will have a register.
if (From->getType() == Subst->getType()) {
// At this point we know the substitution's register is compatible.
return Subst;
}
}
}
}
// Go through the various types of operands:
// OperandMIPS32Mem, 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 *Mem = llvm::dyn_cast<OperandMIPS32Mem>(From)) {
// Base must be in a physical register.
Variable *Base = Mem->getBase();
ConstantInteger32 *Offset = llvm::cast<ConstantInteger32>(Mem->getOffset());
Variable *RegBase = nullptr;
assert(Base);
RegBase = llvm::cast<Variable>(
legalize(Base, Legal_Reg | Legal_Rematerializable));
if (Offset != nullptr && Offset->getValue() != 0) {
static constexpr bool ZeroExt = false;
if (!OperandMIPS32Mem::canHoldOffset(Ty, ZeroExt, Offset->getValue())) {
llvm::report_fatal_error("Invalid memory offset.");
}
}
// Create a new operand if there was a change.
if (Base != RegBase) {
Mem = OperandMIPS32Mem::create(Func, Ty, RegBase, Offset,
Mem->getAddrMode());
}
if (Allowed & Legal_Mem) {
From = Mem;
} else {
Variable *Reg = makeReg(Ty, RegNum);
_lw(Reg, Mem);
From = Reg;
}
return From;
}
if (llvm::isa<Constant>(From)) {
if (auto *C = llvm::dyn_cast<ConstantRelocatable>(From)) {
(void)C;
......@@ -2511,6 +2626,15 @@ Operand *TargetMIPS32::legalize(Operand *From, LegalMask Allowed,
}
if (auto *Var = llvm::dyn_cast<Variable>(From)) {
if (Var->isRematerializable()) {
if (Allowed & Legal_Rematerializable) {
return From;
}
Variable *T = makeReg(Var->getType(), RegNum);
_mov(T, Var);
return T;
}
// 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.
......
......@@ -101,9 +101,7 @@ public:
PrologEmitsFixedAllocas = true;
}
int32_t getFrameFixedAllocaOffset() const override {
// TODO(sehr): Implement fixed stack layout.
llvm::report_fatal_error("Not yet implemented");
return 0;
return FixedAllocaSizeBytes - (SpillAreaSizeBytes - MaxOutArgsSizeBytes);
}
uint32_t maxOutArgsSizeBytes() const override { return MaxOutArgsSizeBytes; }
......@@ -448,6 +446,7 @@ public:
Legal_Reg = 1 << 0, // physical register, not stack location
Legal_Imm = 1 << 1,
Legal_Mem = 1 << 2,
Legal_Rematerializable = 1 << 3,
Legal_Default = ~Legal_None
};
typedef uint32_t LegalMask;
......
......@@ -6,6 +6,13 @@
; RUN: %p2i --filetype=obj --disassemble -i %s --args -O2 | FileCheck %s
; RUN: %p2i --filetype=obj --disassemble -i %s --args -Om1 | FileCheck %s
; RUN: %if --need=target_MIPS32 --need=allow_dump \
; RUN: --command %p2i --filetype=asm --assemble \
; RUN: --disassemble --target mips32 -i %s --args -Om1 --skip-unimplemented \
; RUN: -allow-externally-defined-symbols \
; RUN: | %if --need=target_MIPS32 --need=allow_dump \
; RUN: --command FileCheck --check-prefix MIPS32 %s
define internal float @loadFloat(i32 %a) {
entry:
%__1 = inttoptr i32 %a to float*
......@@ -16,6 +23,9 @@ entry:
; CHECK: movss
; CHECK: fld
; MIPS32-LABEL: loadFloat
; MIPS32: lwc1 $f{{.*}},0{{.*}}
define internal double @loadDouble(i32 %a) {
entry:
%__1 = inttoptr i32 %a to double*
......@@ -26,6 +36,9 @@ entry:
; CHECK: movsd
; CHECK: fld
; MIPS32-LABEL: loadDouble
; MIPS32: ldc1 $f{{.*}},0{{.*}}
define internal void @storeFloat(i32 %a, float %value) {
entry:
%__2 = inttoptr i32 %a to float*
......@@ -36,6 +49,9 @@ entry:
; CHECK: movss
; CHECK: movss
; MIPS32-LABEL: storeFloat
; MIPS32: swc1 $f{{.*}},0{{.*}}
define internal void @storeDouble(i32 %a, double %value) {
entry:
%__2 = inttoptr i32 %a to double*
......@@ -46,6 +62,10 @@ entry:
; CHECK: movsd
; CHECK: movsd
; MIPS32-LABEL: storeDouble
; MIPS32: ldc1 $f{{.*}},4{{.*}}
; MIPS32: sdc1 $f{{.*}},0{{.*}}
define internal void @storeFloatConst(i32 %a) {
entry:
%a.asptr = inttoptr i32 %a to float*
......@@ -56,6 +76,11 @@ entry:
; CHECK: movss
; CHECK: movss
; MIPS32-LABEL: storeFloatConst
; MIPS32: lui {{.*}},{{.*}}
; MIPS32: lwc1 $f{{.*}},{{.*}}
; MIPS32: swc1 $f{{.*}},0{{.*}}
define internal void @storeDoubleConst(i32 %a) {
entry:
%a.asptr = inttoptr i32 %a to double*
......@@ -65,3 +90,8 @@ entry:
; CHECK-LABEL: storeDoubleConst
; CHECK: movsd
; CHECK: movsd
; MIPS32-LABEL: storeDoubleConst
; MIPS32: lui {{.*}},{{.*}}
; MIPS32: ldc1 $f{{.*}},{{.*}}
; MIPS32: sdc1 $f{{.*}},0{{.*}}
......@@ -4,6 +4,13 @@
; RUN: %p2i -i %s --args --verbose inst -threads=0 | FileCheck %s
; RUN: %if --need=target_MIPS32 --need=allow_dump \
; RUN: --command %p2i --filetype=asm --assemble \
; RUN: --disassemble --target mips32 -i %s --args -Om1 --skip-unimplemented \
; RUN: -allow-externally-defined-symbols \
; RUN: | %if --need=target_MIPS32 --need=allow_dump \
; RUN: --command FileCheck --check-prefix MIPS32 %s
define internal void @load_i64(i32 %addr_arg) {
entry:
%__1 = inttoptr i32 %addr_arg to i64*
......@@ -16,6 +23,10 @@ entry:
; CHECK-NEXT: ret void
}
; MIPS32-LABEL: load_i64
; MIPS32: lw {{.*}},0({{.*}})
; MIPS32-NEXT: lw {{.*}},4({{.*}})
define internal void @load_i32(i32 %addr_arg) {
entry:
%__1 = inttoptr i32 %addr_arg to i32*
......@@ -28,6 +39,9 @@ entry:
; CHECK-NEXT: ret void
}
; MIPS32-LABEL: load_i32
; MIPS32: lw {{.*}},0({{.*}})
define internal void @load_i16(i32 %addr_arg) {
entry:
%__1 = inttoptr i32 %addr_arg to i16*
......@@ -40,6 +54,9 @@ entry:
; CHECK-NEXT: ret void
}
; MIPS32-LABEL: load_i16
; MIPS32: lh {{.*}},0({{.*}})
define internal void @load_i8(i32 %addr_arg) {
entry:
%__1 = inttoptr i32 %addr_arg to i8*
......@@ -51,3 +68,6 @@ entry:
; CHECK-NEXT: %iv = load i8, i8* %addr_arg, align 1
; CHECK-NEXT: ret void
}
; MIPS32-LABEL: load_i8
; MIPS32: lb {{.*}},0({{.*}})
......@@ -56,7 +56,7 @@ entry:
}
; MIPS32-LABEL: store_i16
; MIPS32: li
; MIPS32: sw
; MIPS32: sh
define internal void @store_i8(i32 %addr_arg) {
entry:
......@@ -71,4 +71,34 @@ entry:
}
; MIPS32-LABEL: store_i8
; MIPS32: li
; MIPS32: sw
\ No newline at end of file
; MIPS32: sb
define internal void @store_f32(float* %faddr_arg) {
entry:
store float 1.000000e+00, float* %faddr_arg, align 4
ret void
; CHECK: Initial CFG
; CHECK: entry:
; CHECK-NEXT: store float 1.000000e+00, float* %faddr_arg, align 4
; CHECK-NEXT: ret void
}
; MIPS32-LABEL: store_f32
; MIPS32: lui
; MIPS32: lwc1
; MIPS32: swc1
define internal void @store_f64(double* %daddr_arg) {
entry:
store double 1.000000e+00, double* %daddr_arg, align 8
ret void
; CHECK: Initial CFG
; CHECK: entry:
; CHECK-NEXT: store double 1.000000e+00, double* %daddr_arg, align 8
; CHECK-NEXT: ret void
}
; MIPS32-LABEL: store_f64
; MIPS32: lui
; MIPS32: ldc1
; MIPS32: sdc1
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