Commit 00e36049 by Reed Kotler Committed by Jim Stichnoth

Subzero: Mips: Lower some i64 arithmetic instructions.

This patch is a MIPS version of this part of ARM patch: https://codereview.chromium.org/1151663004/ BUG= R=stichnot@chromium.org Review URL: https://codereview.chromium.org/1640913004 . Patch from Reed Kotler <rkotlerimgtec@gmail.com>.
parent c0487128
......@@ -54,13 +54,22 @@ template <> const char *InstMIPS32Lui::Opcode = "lui";
template <> const char *InstMIPS32La::Opcode = "la";
// Three-addr ops
template <> const char *InstMIPS32Add::Opcode = "add";
template <> const char *InstMIPS32Addu::Opcode = "addu";
template <> const char *InstMIPS32And::Opcode = "and";
template <> const char *InstMIPS32Mul::Opcode = "mul";
template <> const char *InstMIPS32Or::Opcode = "or";
template <> const char *InstMIPS32Ori::Opcode = "ori";
template <> const char *InstMIPS32Sltu::Opcode = "sltu";
template <> const char *InstMIPS32Sub::Opcode = "sub";
template <> const char *InstMIPS32Subu::Opcode = "subu";
template <> const char *InstMIPS32Xor::Opcode = "xor";
InstMIPS32Call::InstMIPS32Call(Cfg *Func, Variable *Dest, Operand *CallTarget)
: InstMIPS32(Func, InstMIPS32::Call, 1, Dest) {
HasSideEffects = true;
addSource(CallTarget);
}
InstMIPS32Mov::InstMIPS32Mov(Cfg *Func, Variable *Dest, Operand *Src)
: InstMIPS32(Func, InstMIPS32::Mov, 2, Dest) {
auto *Dest64 = llvm::dyn_cast<Variable64On32>(Dest);
......@@ -145,6 +154,48 @@ void InstMIPS32Ret::emit(const Cfg *Func) const {
RA->emit(Func);
}
void InstMIPS32Call::emit(const Cfg *Func) const {
if (!BuildDefs::dump())
return;
Ostream &Str = Func->getContext()->getStrEmit();
assert(getSrcSize() == 1);
if (llvm::isa<ConstantInteger32>(getCallTarget())) {
// This shouldn't happen (typically have to copy the full 32-bits to a
// register and do an indirect jump).
llvm::report_fatal_error("MIPS2Call to ConstantInteger32");
} else if (const auto *CallTarget =
llvm::dyn_cast<ConstantRelocatable>(getCallTarget())) {
// Calls only have 24-bits, but the linker should insert veneers to extend
// the range if needed.
Str << "\t"
"jal"
"\t";
CallTarget->emitWithoutPrefix(Func->getTarget());
} else {
Str << "\t"
"jal"
"\t";
getCallTarget()->emit(Func);
}
}
void InstMIPS32Call::emitIAS(const Cfg *Func) const {
(void)Func;
llvm_unreachable("Not yet implemented");
}
void InstMIPS32Call::dump(const Cfg *Func) const {
if (!BuildDefs::dump())
return;
Ostream &Str = Func->getContext()->getStrDump();
if (getDest()) {
dumpDest(Func);
Str << " = ";
}
Str << "call ";
getCallTarget()->dump(Func);
}
void InstMIPS32Ret::emitIAS(const Cfg *Func) const {
(void)Func;
llvm_unreachable("Not yet implemented");
......
......@@ -118,8 +118,10 @@ public:
enum InstKindMIPS32 {
k__Start = Inst::Target,
Add,
Addu,
And,
Addiu,
Call,
La,
Lui,
Mov, // actually a pseudo op for addi rd, rs, 0
......@@ -128,6 +130,8 @@ public:
Ori,
Ret,
Sub,
Subu,
Sltu,
Xor
};
......@@ -139,6 +143,9 @@ public:
Str << Opcode << "." << Ty;
}
// TODO(rkotler): while branching is not implemented
bool repointEdges(CfgNode *, CfgNode *) override { return true; }
/// Shared emit routines for common forms of instructions.
static void emitUnaryopGPR(const char *Opcode, const InstMIPS32 *Inst,
const Cfg *Func);
......@@ -273,6 +280,27 @@ private:
static const char *Opcode;
};
class InstMIPS32Call : public InstMIPS32 {
InstMIPS32Call() = delete;
InstMIPS32Call(const InstMIPS32Call &) = delete;
InstMIPS32Call &operator=(const InstMIPS32Call &) = delete;
public:
static InstMIPS32Call *create(Cfg *Func, Variable *Dest,
Operand *CallTarget) {
return new (Func->allocate<InstMIPS32Call>())
InstMIPS32Call(Func, Dest, CallTarget);
}
Operand *getCallTarget() const { return getSrc(0); }
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, Call); }
private:
InstMIPS32Call(Cfg *Func, Variable *Dest, Operand *CallTarget);
};
template <InstMIPS32::InstKindMIPS32 K, bool Signed = false>
class InstMIPS32Imm16 : public InstMIPS32 {
InstMIPS32Imm16() = delete;
......@@ -346,6 +374,7 @@ private:
};
using InstMIPS32Add = InstMIPS32ThreeAddrGPR<InstMIPS32::Add>;
using InstMIPS32Addu = InstMIPS32ThreeAddrGPR<InstMIPS32::Addu>;
using InstMIPS32Addiu = InstMIPS32Imm16<InstMIPS32::Addiu, true>;
using InstMIPS32And = InstMIPS32ThreeAddrGPR<InstMIPS32::And>;
using InstMIPS32Lui = InstMIPS32Imm16<InstMIPS32::Lui>;
......@@ -353,7 +382,9 @@ using InstMIPS32La = InstMIPS32UnaryopGPR<InstMIPS32::La>;
using InstMIPS32Mul = InstMIPS32ThreeAddrGPR<InstMIPS32::Mul>;
using InstMIPS32Or = InstMIPS32ThreeAddrGPR<InstMIPS32::Or>;
using InstMIPS32Ori = InstMIPS32Imm16<InstMIPS32::Ori>;
using InstMIPS32Sltu = InstMIPS32ThreeAddrGPR<InstMIPS32::Sltu>;
using InstMIPS32Sub = InstMIPS32ThreeAddrGPR<InstMIPS32::Sub>;
using InstMIPS32Subu = InstMIPS32ThreeAddrGPR<InstMIPS32::Subu>;
using InstMIPS32Ori = InstMIPS32Imm16<InstMIPS32::Ori>;
using InstMIPS32Xor = InstMIPS32ThreeAddrGPR<InstMIPS32::Xor>;
......
......@@ -571,13 +571,101 @@ void TargetMIPS32::lowerAlloca(const InstAlloca *Inst) {
UnimplementedLoweringError(this, Inst);
}
void TargetMIPS32::lowerInt64Arithmetic(const InstArithmetic *Inst,
Variable *Dest, Operand *Src0,
Operand *Src1) {
InstArithmetic::OpKind Op = Inst->getOp();
switch (Op) {
case InstArithmetic::Add:
case InstArithmetic::And:
case InstArithmetic::Or:
case InstArithmetic::Sub:
case InstArithmetic::Xor:
break;
default:
UnimplementedLoweringError(this, Inst);
return;
}
auto *DestLo = llvm::cast<Variable>(loOperand(Dest));
auto *DestHi = llvm::cast<Variable>(hiOperand(Dest));
Variable *Src0LoR = legalizeToReg(loOperand(Src0));
Variable *Src1LoR = legalizeToReg(loOperand(Src1));
Variable *Src0HiR = legalizeToReg(hiOperand(Src0));
Variable *Src1HiR = legalizeToReg(hiOperand(Src1));
switch (Op) {
case InstArithmetic::_num:
llvm::report_fatal_error("Unknown arithmetic operator");
return;
case InstArithmetic::Add: {
Variable *T_Carry = makeReg(IceType_i32);
Variable *T_Lo = makeReg(IceType_i32);
Variable *T_Hi = makeReg(IceType_i32);
Variable *T_Hi2 = makeReg(IceType_i32);
_addu(T_Lo, Src0LoR, Src1LoR);
_mov(DestLo, T_Lo);
_sltu(T_Carry, T_Lo, Src0LoR);
_addu(T_Hi, T_Carry, Src0HiR);
_addu(T_Hi2, Src1HiR, T_Hi);
_mov(DestHi, T_Hi2);
return;
}
case InstArithmetic::And: {
Variable *T_Lo = makeReg(IceType_i32);
Variable *T_Hi = makeReg(IceType_i32);
_and(T_Lo, Src0LoR, Src1LoR);
_mov(DestLo, T_Lo);
_and(T_Hi, Src0HiR, Src1HiR);
_mov(DestHi, T_Hi);
return;
}
case InstArithmetic::Sub: {
Variable *T_Borrow = makeReg(IceType_i32);
Variable *T_Lo = makeReg(IceType_i32);
Variable *T_Hi = makeReg(IceType_i32);
Variable *T_Hi2 = makeReg(IceType_i32);
_subu(T_Lo, Src0LoR, Src1LoR);
_mov(DestLo, T_Lo);
_sltu(T_Borrow, Src0LoR, Src1LoR);
_addu(T_Hi, T_Borrow, Src1HiR);
_subu(T_Hi2, Src0HiR, T_Hi);
_mov(DestHi, T_Hi2);
return;
}
case InstArithmetic::Or: {
Variable *T_Lo = makeReg(IceType_i32);
Variable *T_Hi = makeReg(IceType_i32);
_or(T_Lo, Src0LoR, Src1LoR);
_mov(DestLo, T_Lo);
_or(T_Hi, Src0HiR, Src1HiR);
_mov(DestHi, T_Hi);
return;
}
case InstArithmetic::Xor: {
Variable *T_Lo = makeReg(IceType_i32);
Variable *T_Hi = makeReg(IceType_i32);
_xor(T_Lo, Src0LoR, Src1LoR);
_mov(DestLo, T_Lo);
_xor(T_Hi, Src0HiR, Src1HiR);
_mov(DestHi, T_Hi);
return;
}
default:
UnimplementedLoweringError(this, Inst);
return;
}
}
void TargetMIPS32::lowerArithmetic(const InstArithmetic *Inst) {
Variable *Dest = Inst->getDest();
// We need to signal all the UnimplementedLoweringError errors before any
// legalization into new variables, otherwise Om1 register allocation may fail
// when it sees variables that are defined but not used.
if (Dest->getType() == IceType_i64) {
UnimplementedLoweringError(this, Inst);
Type DestTy = Dest->getType();
Operand *Src0 = legalizeUndef(Inst->getSrc(0));
Operand *Src1 = legalizeUndef(Inst->getSrc(1));
if (DestTy == IceType_i64) {
lowerInt64Arithmetic(Inst, Inst->getDest(), Src0, Src1);
return;
}
if (isVectorType(Dest->getType())) {
......@@ -606,8 +694,6 @@ void TargetMIPS32::lowerArithmetic(const InstArithmetic *Inst) {
// At this point Dest->getType() is non-i64 scalar
Variable *T = makeReg(Dest->getType());
Operand *Src0 = legalizeUndef(Inst->getSrc(0));
Operand *Src1 = legalizeUndef(Inst->getSrc(1));
Variable *Src0R = legalizeToReg(Src0);
Variable *Src1R = legalizeToReg(Src1);
......@@ -615,7 +701,7 @@ void TargetMIPS32::lowerArithmetic(const InstArithmetic *Inst) {
case InstArithmetic::_num:
break;
case InstArithmetic::Add:
_add(T, Src0R, Src1R);
_addu(T, Src0R, Src1R);
_mov(Dest, T);
return;
case InstArithmetic::And:
......@@ -631,7 +717,7 @@ void TargetMIPS32::lowerArithmetic(const InstArithmetic *Inst) {
_mov(Dest, T);
return;
case InstArithmetic::Sub:
_sub(T, Src0R, Src1R);
_subu(T, Src0R, Src1R);
_mov(Dest, T);
return;
case InstArithmetic::Mul: {
......@@ -664,6 +750,7 @@ void TargetMIPS32::lowerArithmetic(const InstArithmetic *Inst) {
case InstArithmetic::Frem:
break;
}
UnimplementedLoweringError(this, Inst);
}
void TargetMIPS32::lowerAssign(const InstAssign *Inst) {
......@@ -708,8 +795,91 @@ void TargetMIPS32::lowerBr(const InstBr *Inst) {
UnimplementedLoweringError(this, Inst);
}
void TargetMIPS32::lowerCall(const InstCall *Inst) {
UnimplementedLoweringError(this, Inst);
void TargetMIPS32::lowerCall(const InstCall *Instr) {
// TODO(rkotler): assign arguments to registers and stack. Also reserve stack.
if (Instr->getNumArgs()) {
UnimplementedLoweringError(this, Instr);
return;
}
// Generate the call instruction. Assign its result to a temporary with high
// register allocation weight.
Variable *Dest = Instr->getDest();
// ReturnReg doubles as ReturnRegLo as necessary.
Variable *ReturnReg = nullptr;
Variable *ReturnRegHi = nullptr;
if (Dest) {
switch (Dest->getType()) {
case IceType_NUM:
llvm_unreachable("Invalid Call dest type");
return;
case IceType_void:
break;
case IceType_i1:
case IceType_i8:
case IceType_i16:
case IceType_i32:
ReturnReg = makeReg(Dest->getType(), RegMIPS32::Reg_V0);
break;
case IceType_i64:
ReturnReg = makeReg(IceType_i32, RegMIPS32::Reg_V0);
ReturnRegHi = makeReg(IceType_i32, RegMIPS32::Reg_V1);
break;
case IceType_f32:
case IceType_f64:
UnimplementedLoweringError(this, Instr);
return;
case IceType_v4i1:
case IceType_v8i1:
case IceType_v16i1:
case IceType_v16i8:
case IceType_v8i16:
case IceType_v4i32:
case IceType_v4f32:
UnimplementedLoweringError(this, Instr);
return;
}
}
Operand *CallTarget = Instr->getCallTarget();
// Allow ConstantRelocatable to be left alone as a direct call,
// but force other constants like ConstantInteger32 to be in
// a register and make it an indirect call.
if (!llvm::isa<ConstantRelocatable>(CallTarget)) {
CallTarget = legalize(CallTarget, Legal_Reg);
}
Inst *NewCall = InstMIPS32Call::create(Func, ReturnReg, CallTarget);
Context.insert(NewCall);
if (ReturnRegHi)
Context.insert(InstFakeDef::create(Func, ReturnRegHi));
// Insert a register-kill pseudo instruction.
Context.insert(InstFakeKill::create(Func, NewCall));
// Generate a FakeUse to keep the call live if necessary.
if (Instr->hasSideEffects() && ReturnReg) {
Context.insert<InstFakeDef>(ReturnReg);
}
if (Dest == nullptr)
return;
// Assign the result of the call to Dest.
if (ReturnReg) {
if (ReturnRegHi) {
assert(Dest->getType() == IceType_i64);
auto *Dest64On32 = llvm::cast<Variable64On32>(Dest);
Variable *DestLo = Dest64On32->getLo();
Variable *DestHi = Dest64On32->getHi();
_mov(DestLo, ReturnReg);
_mov(DestHi, ReturnRegHi);
} else {
assert(Dest->getType() == IceType_i32 || Dest->getType() == IceType_i16 ||
Dest->getType() == IceType_i8 || Dest->getType() == IceType_i1 ||
isVectorType(Dest->getType()));
if (isFloatingType(Dest->getType()) || isVectorType(Dest->getType())) {
UnimplementedLoweringError(this, Instr);
return;
} else {
_mov(Dest, ReturnReg);
}
}
}
}
void TargetMIPS32::lowerCast(const InstCast *Inst) {
......@@ -980,8 +1150,8 @@ void TargetMIPS32::prelowerPhis() {
void TargetMIPS32::postLower() {
if (Ctx->getFlags().getOptLevel() == Opt_m1)
return;
// Find two-address non-SSA instructions where Dest==Src0, and set the
// IsDestRedefined flag to keep liveness analysis consistent.
// TODO(rkotler): Find two-address non-SSA instructions where Dest==Src0,
// and set the IsDestRedefined flag to keep liveness analysis consistent.
UnimplementedError(Func->getContext()->getFlags());
}
......@@ -1087,7 +1257,9 @@ Operand *TargetMIPS32::legalize(Operand *From, LegalMask Allowed,
else
Reg = getPhysicalRegister(RegNum);
if (isInt<16>(int32_t(Value))) {
_addiu(Reg, getPhysicalRegister(RegMIPS32::Reg_ZERO, Ty), Value);
Variable *Zero = getPhysicalRegister(RegMIPS32::Reg_ZERO, Ty);
Context.insert<InstFakeDef>(Zero);
_addiu(Reg, Zero, Value);
} else {
uint32_t UpperBits = (Value >> 16) & 0xFFFF;
(void)UpperBits;
......
......@@ -137,6 +137,10 @@ public:
Context.insert<InstMIPS32Add>(Dest, Src0, Src1);
}
void _addu(Variable *Dest, Variable *Src0, Variable *Src1) {
Context.insert<InstMIPS32Addu>(Dest, Src0, Src1);
}
void _and(Variable *Dest, Variable *Src0, Variable *Src1) {
Context.insert<InstMIPS32And>(Dest, Src0, Src1);
}
......@@ -185,6 +189,14 @@ public:
Context.insert<InstMIPS32Sub>(Dest, Src0, Src1);
}
void _sltu(Variable *Dest, Variable *Src0, Variable *Src1) {
Context.insert<InstMIPS32Sltu>(Dest, Src0, Src1);
}
void _subu(Variable *Dest, Variable *Src0, Variable *Src1) {
Context.insert<InstMIPS32Subu>(Dest, Src0, Src1);
}
void _xor(Variable *Dest, Variable *Src0, Variable *Src1) {
Context.insert<InstMIPS32Xor>(Dest, Src0, Src1);
}
......@@ -235,6 +247,8 @@ protected:
void lowerAlloca(const InstAlloca *Inst) override;
void lowerArithmetic(const InstArithmetic *Inst) override;
void lowerInt64Arithmetic(const InstArithmetic *Inst, Variable *Dest,
Operand *Src0, Operand *Src1);
void lowerAssign(const InstAssign *Inst) override;
void lowerBr(const InstBr *Inst) override;
void lowerCall(const InstCall *Inst) override;
......
......@@ -26,6 +26,16 @@
; RUN: | %if --need=target_ARM32 --need=allow_dump \
; RUN: --command FileCheck --check-prefix ARM32 --check-prefix ARM32-OM1 %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
@__init_array_start = internal constant [0 x i8] zeroinitializer, align 4
@__fini_array_start = internal constant [0 x i8] zeroinitializer, align 4
@__tls_template_start = internal constant [0 x i8] zeroinitializer, align 8
......@@ -36,6 +46,9 @@ entry:
ret i32 %b
}
; MIPS32-LABEL: ignore64BitArg
; MIPS32: move v0,a2
define internal i32 @pass64BitArg(i64 %a, i64 %b, i64 %c, i64 %d, i64 %e, i64 %f) {
entry:
%call = call i32 @ignore64BitArgNoInline(i64 %a, i32 123, i64 %b)
......@@ -102,6 +115,8 @@ entry:
; ARM32: mov r2, #123
; ARM32: bl {{.*}} ignore64BitArgNoInline
; MIPS32-LABEL: pass64BitArg
declare i32 @ignore64BitArgNoInline(i64, i32, i64)
......@@ -167,6 +182,9 @@ entry:
; ARM32: mov {{.*}}, #123
; ARM32: bl {{.*}} ignore64BitArgNoInline
; MIPS32-LABEL: pass64BitUndefArg
; MIPS32: jr ra
define internal i64 @return64BitArg(i64 %padding, i64 %a) {
entry:
ret i64 %a
......@@ -184,6 +202,11 @@ entry:
; ARM32: mov {{.*}}, r3
; ARM32: bx lr
; MIPS32-LABEL; return64BitArg
; MIPS32: move v0,a2
; MIPS32: move v1,a3
; MIPS32: jr ra
define internal i64 @return64BitConst() {
entry:
ret i64 -2401053092306725256
......@@ -202,6 +225,13 @@ entry:
; ARM32: movw r1, #48879 ; 0xbeef
; ARM32: movt r1, #57005 ; 0xdead
; MIPS32-LABEL: return64BitConst
; MIPS32: lui v0,0x1234
; MIPS32: ori v0,v0,0x5678
; MIPS32: lui v1,0xdead
; MIPS32: ori v1,v1,0xbeef
; MIPS32: jr ra
define internal i64 @add64BitSigned(i64 %a, i64 %b) {
entry:
%add = add i64 %b, %a
......@@ -219,6 +249,12 @@ entry:
; ARM32: adds
; ARM32: adc
; MIPS32-LABEL: add64BitSigned
; MIPS32: addu
; MIPS32: sltu
; MIPS32: addu
; MIPS32: addu
define internal i64 @add64BitUnsigned(i64 %a, i64 %b) {
entry:
%add = add i64 %b, %a
......@@ -236,6 +272,12 @@ entry:
; ARM32: adds
; ARM32: adc
; MIPS32-LABEL: add64BitUnsigned
; MIPS32: addu
; MIPS32: sltu
; MIPS32: addu
; MIPS32: addu
define internal i64 @sub64BitSigned(i64 %a, i64 %b) {
entry:
%sub = sub i64 %a, %b
......@@ -253,6 +295,12 @@ entry:
; ARM32: subs
; ARM32: sbc
; MIPS32-LABEL: sub64BitSigned
; MIPS32: subu
; MIPS32: sltu
; MIPS32: addu
; MIPS32: subu
define internal i64 @sub64BitUnsigned(i64 %a, i64 %b) {
entry:
%sub = sub i64 %a, %b
......@@ -270,6 +318,12 @@ entry:
; ARM32: subs
; ARM32: sbc
; MIPS32-LABEL: sub64BitUnsigned
; MIPS32: subu
; MIPS32: sltu
; MIPS32: addu
; MIPS32: subu
define internal i64 @mul64BitSigned(i64 %a, i64 %b) {
entry:
%mul = mul i64 %b, %a
......@@ -604,6 +658,10 @@ entry:
; ARM32: and
; ARM32: and
; MIPS32-LABEL: and64BitSigned
; MIPS32: and
; MIPS32: and
define internal i64 @and64BitUnsigned(i64 %a, i64 %b) {
entry:
%and = and i64 %b, %a
......@@ -621,6 +679,10 @@ entry:
; ARM32: and
; ARM32: and
; MIPS32-LABEL: and64BitUnsigned
; MIPS32: and
; MIPS32: and
define internal i64 @or64BitSigned(i64 %a, i64 %b) {
entry:
%or = or i64 %b, %a
......@@ -638,6 +700,10 @@ entry:
; ARM32: orr
; ARM32: orr
; MIPS32-LABEL: or64BitSigned
; MIPS32: or
; MIPS32: or
define internal i64 @or64BitUnsigned(i64 %a, i64 %b) {
entry:
%or = or i64 %b, %a
......@@ -655,6 +721,10 @@ entry:
; ARM32: orr
; ARM32: orr
; MIPS32-LABEL: or64BitUnsigned
; MIPS32: or
; MIPS32: or
define internal i64 @xor64BitSigned(i64 %a, i64 %b) {
entry:
%xor = xor i64 %b, %a
......@@ -672,6 +742,10 @@ entry:
; ARM32: eor
; ARM32: eor
; MIPS32-LABEL: xor64BitSigned
; MIPS32: xor
; MIPS32: xor
define internal i64 @xor64BitUnsigned(i64 %a, i64 %b) {
entry:
%xor = xor i64 %b, %a
......@@ -689,6 +763,10 @@ entry:
; ARM32: eor
; ARM32: eor
; MIPS32-LABEL: xor64BitUnsigned
; MIPS32: xor
; MIPS32: xor
define internal i32 @trunc64To32Signed(i64 %padding, i64 %a) {
entry:
%conv = trunc i64 %a to i32
......
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