Commit 5cce7619 by Sagar Thakur Committed by Jim Stichnoth

Added bool folding machinery for MIPS32.

Added implementation for conditional branch instructions. R=jpp@chromium.org, stichnot@chromium.org Review URL: https://codereview.chromium.org/1993773004 . Patch from Sagar Thakur <sagar.thakur@imgtec.com>.
parent ae93eee8
//===- subzero/src/IceConditionCodesMIPS32.h - Condition Codes --*- C++ -*-===//
//
// The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Declares the condition codes for MIPS32.
///
//===----------------------------------------------------------------------===//
#ifndef SUBZERO_SRC_ICECONDITIONCODESMIPS32_H
#define SUBZERO_SRC_ICECONDITIONCODESMIPS32_H
#include "IceDefs.h"
#include "IceInstMIPS32.def"
namespace Ice {
class CondMIPS32 {
CondMIPS32() = delete;
CondMIPS32(const CondMIPS32 &) = delete;
CondMIPS32 &operator=(const CondMIPS32 &) = delete;
public:
/// An enum of codes used for conditional instructions. The enum value should
/// match the value used to encode operands in binary instructions.
enum Cond {
#define X(tag, opp, emit) tag,
ICEINSTMIPS32COND_TABLE
#undef X
};
static bool isDefined(Cond C) { return C != kNone; }
static bool isUnconditional(Cond C) { return !isDefined(C) || C == AL; }
};
} // end of namespace Ice
#endif // SUBZERO_SRC_ICECONDITIONCODESMIPS32_H
......@@ -25,6 +25,17 @@
namespace Ice {
namespace MIPS32 {
const struct InstMIPS32CondAttributes_ {
CondMIPS32::Cond Opposite;
const char *EmitString;
} InstMIPS32CondAttributes[] = {
#define X(tag, opp, emit) \
{ CondMIPS32::opp, emit } \
,
ICEINSTMIPS32COND_TABLE
#undef X
};
bool OperandMIPS32Mem::canHoldOffset(Type Ty, bool SignExt, int32_t Offset) {
(void)SignExt;
(void)Ty;
......@@ -121,9 +132,27 @@ template <> void InstMIPS32Multu::emit(const Cfg *Func) const {
InstMIPS32Br::InstMIPS32Br(Cfg *Func, const CfgNode *TargetTrue,
const CfgNode *TargetFalse,
const InstMIPS32Label *Label)
const InstMIPS32Label *Label, CondMIPS32::Cond Cond)
: InstMIPS32(Func, InstMIPS32::Br, 0, nullptr), TargetTrue(TargetTrue),
TargetFalse(TargetFalse), Label(Label) {}
TargetFalse(TargetFalse), Label(Label), Predicate(Cond) {}
InstMIPS32Br::InstMIPS32Br(Cfg *Func, const CfgNode *TargetTrue,
const CfgNode *TargetFalse, Operand *Src0,
const InstMIPS32Label *Label, CondMIPS32::Cond Cond)
: InstMIPS32(Func, InstMIPS32::Br, 1, nullptr), TargetTrue(TargetTrue),
TargetFalse(TargetFalse), Label(Label), Predicate(Cond) {
addSource(Src0);
}
InstMIPS32Br::InstMIPS32Br(Cfg *Func, const CfgNode *TargetTrue,
const CfgNode *TargetFalse, Operand *Src0,
Operand *Src1, const InstMIPS32Label *Label,
CondMIPS32::Cond Cond)
: InstMIPS32(Func, InstMIPS32::Br, 2, nullptr), TargetTrue(TargetTrue),
TargetFalse(TargetFalse), Label(Label), Predicate(Cond) {
addSource(Src0);
addSource(Src1);
}
InstMIPS32Label::InstMIPS32Label(Cfg *Func, TargetMIPS32 *Target)
: InstMIPS32(Func, InstMIPS32::Label, 0, nullptr),
......@@ -280,15 +309,56 @@ void InstMIPS32Br::emit(const Cfg *Func) const {
return;
Ostream &Str = Func->getContext()->getStrEmit();
Str << "\t"
"b"
<< "\t";
"b" << InstMIPS32CondAttributes[Predicate].EmitString << "\t";
if (Label) {
Str << Label->getLabelName();
} else {
if (isUnconditionalBranch()) {
Str << getTargetFalse()->getAsmName();
} else {
// TODO(reed kotler): Finish implementing conditional branch.
switch (Predicate) {
default:
break;
case CondMIPS32::EQ:
case CondMIPS32::NE: {
getSrc(0)->emit(Func);
Str << ", ";
getSrc(1)->emit(Func);
Str << ", ";
break;
}
case CondMIPS32::EQZ:
case CondMIPS32::NEZ:
case CondMIPS32::LEZ:
case CondMIPS32::LTZ:
case CondMIPS32::GEZ:
case CondMIPS32::GTZ: {
getSrc(0)->emit(Func);
Str << ", ";
break;
}
}
Str << getTargetFalse()->getAsmName();
}
}
}
void InstMIPS32Br::dump(const Cfg *Func) const {
if (!BuildDefs::dump())
return;
Ostream &Str = Func->getContext()->getStrDump();
Str << "\t"
"b" << InstMIPS32CondAttributes[Predicate].EmitString << "\t";
if (Label) {
Str << Label->getLabelName();
} else {
if (isUnconditionalBranch()) {
Str << getTargetFalse()->getAsmName();
} else {
dumpSources(Func);
Str << ", ";
Str << getTargetFalse()->getAsmName();
}
}
}
......
//===- subzero/src/IceInstMIPS32.def - X-Macros for MIPS32 insts --*- C++ -*-===//
//===- subzero/src/IceInstMIPS32.def - X-Macros for MIPS insts --*- C++ -*-===//
//
// The Subzero Code Generator
//
......@@ -171,7 +171,18 @@
X(Reg_I64PAIR_Last, = Reg_LOHI) \
//define X(val, init)
// TODO(reed kotler): add condition code tables, etc.
#define ICEINSTMIPS32COND_TABLE \
/* enum value, opposite, emit */ \
X(EQ, NE, "eq") /* equal */ \
X(NE, EQ, "ne") /* not equal */ \
X(EQZ, NEZ, "eqz") /* signed equal to zero */ \
X(NEZ, EQZ, "nez") /* signed not equal to zero */ \
X(GEZ, LTZ, "gez") /* signed greater than or equal to zero */ \
X(LTZ, GEZ, "ltz") /* signed less than to zero */ \
X(GTZ, LEZ, "gtz") /* signed greater than to zero */ \
X(LEZ, GTZ, "lez") /* signed less than or equal to zero */ \
X(AL, kNone, "") /* always (unconditional) */ \
X(kNone, kNone, "??") /* special condition / none */
//#define X(tag, opp, emit)
#endif // SUBZERO_SRC_ICEINSTMIPS32_DEF
......@@ -19,6 +19,7 @@
#ifndef SUBZERO_SRC_ICEINSTMIPS32_H
#define SUBZERO_SRC_ICEINSTMIPS32_H
#include "IceConditionCodesMIPS32.h"
#include "IceDefs.h"
#include "IceInst.h"
#include "IceInstMIPS32.def"
......@@ -349,12 +350,32 @@ public:
constexpr CfgNode *NoCondTarget = nullptr;
constexpr InstMIPS32Label *NoLabel = nullptr;
return new (Func->allocate<InstMIPS32Br>())
InstMIPS32Br(Func, NoCondTarget, Target, NoLabel);
InstMIPS32Br(Func, NoCondTarget, Target, NoLabel, CondMIPS32::AL);
}
/// Create a conditional branch to the false node.
static InstMIPS32Br *create(Cfg *Func, CfgNode *TargetTrue,
CfgNode *TargetFalse, Operand *Src0,
Operand *Src1, CondMIPS32::Cond Cond) {
constexpr InstMIPS32Label *NoLabel = nullptr;
return new (Func->allocate<InstMIPS32Br>())
InstMIPS32Br(Func, TargetTrue, TargetFalse, Src0, Src1, NoLabel, Cond);
}
static InstMIPS32Br *create(Cfg *Func, CfgNode *TargetTrue,
CfgNode *TargetFalse, Operand *Src0,
CondMIPS32::Cond Cond) {
constexpr InstMIPS32Label *NoLabel = nullptr;
return new (Func->allocate<InstMIPS32Br>())
InstMIPS32Br(Func, TargetTrue, TargetFalse, Src0, NoLabel, Cond);
}
const CfgNode *getTargetTrue() const { return TargetTrue; }
const CfgNode *getTargetFalse() const { return TargetFalse; }
bool isUnconditionalBranch() const override { return true; }
bool isUnconditionalBranch() const override {
return Predicate == CondMIPS32::AL;
}
bool repointEdges(CfgNode *OldNode, CfgNode *NewNode) override {
(void)OldNode;
(void)NewNode;
......@@ -362,16 +383,25 @@ public:
};
void emit(const Cfg *Func) const override;
void emitIAS(const Cfg *Func) const override { (void)Func; };
void dump(const Cfg *Func) const override { (void)Func; };
void dump(const Cfg *Func) const override;
static bool classof(const Inst *Instr) { return isClassof(Instr, Br); }
private:
InstMIPS32Br(Cfg *Func, const CfgNode *TargetTrue, const CfgNode *TargetFalse,
const InstMIPS32Label *Label);
const InstMIPS32Label *Label, const CondMIPS32::Cond Cond);
InstMIPS32Br(Cfg *Func, const CfgNode *TargetTrue, const CfgNode *TargetFalse,
Operand *Src0, const InstMIPS32Label *Label,
const CondMIPS32::Cond Cond);
InstMIPS32Br(Cfg *Func, const CfgNode *TargetTrue, const CfgNode *TargetFalse,
Operand *Src0, Operand *Src1, const InstMIPS32Label *Label,
const CondMIPS32::Cond Cond);
const CfgNode *TargetTrue;
const CfgNode *TargetFalse;
const InstMIPS32Label *Label; // Intra-block branch target
const CondMIPS32::Cond Predicate;
};
class InstMIPS32Call : public InstMIPS32 {
......
......@@ -21,6 +21,7 @@
#include "IceELFObjectWriter.h"
#include "IceGlobalInits.h"
#include "IceInstMIPS32.h"
#include "IceInstVarIter.h"
#include "IceLiveness.h"
#include "IceOperand.h"
#include "IcePhiLoweringImpl.h"
......@@ -834,7 +835,84 @@ void TargetMIPS32::lowerBr(const InstBr *Instr) {
_br(Instr->getTargetUnconditional());
return;
}
UnimplementedLoweringError(this, Instr);
CfgNode *TargetTrue = Instr->getTargetTrue();
CfgNode *TargetFalse = Instr->getTargetFalse();
Operand *Boolean = Instr->getCondition();
const Inst *Producer = Computations.getProducerOf(Boolean);
if (Producer == nullptr) {
// Since we don't know the producer of this boolean we will assume its
// producer will keep it in positive logic and just emit beqz with this
// Boolean as an operand.
auto *BooleanR = legalizeToReg(Boolean);
_br(TargetTrue, TargetFalse, BooleanR, CondMIPS32::Cond::EQZ);
return;
}
if (Producer->getKind() == Inst::Icmp) {
const InstIcmp *CompareInst = llvm::cast<InstIcmp>(Producer);
Operand *Src0 = CompareInst->getSrc(0);
Operand *Src1 = CompareInst->getSrc(1);
const Type Src0Ty = Src0->getType();
assert(Src0Ty == Src1->getType());
if (Src0Ty == IceType_i64) {
UnimplementedLoweringError(this, Instr);
return;
}
auto *Src0R = legalizeToReg(Src0);
auto *Src1R = legalizeToReg(Src1);
auto *DestT = makeReg(Src0Ty);
switch (CompareInst->getCondition()) {
default:
break;
case InstIcmp::Eq: {
_br(TargetTrue, TargetFalse, Src0R, Src1R, CondMIPS32::Cond::NE);
break;
}
case InstIcmp::Ne: {
_br(TargetTrue, TargetFalse, Src0R, Src1R, CondMIPS32::Cond::EQ);
break;
}
case InstIcmp::Ugt: {
_sltu(DestT, Src1R, Src0R);
_br(TargetTrue, TargetFalse, DestT, CondMIPS32::Cond::EQZ);
break;
}
case InstIcmp::Uge: {
_sltu(DestT, Src0R, Src1R);
_br(TargetTrue, TargetFalse, DestT, CondMIPS32::Cond::NEZ);
break;
}
case InstIcmp::Ult: {
_sltu(DestT, Src0R, Src1R);
_br(TargetTrue, TargetFalse, DestT, CondMIPS32::Cond::EQZ);
break;
}
case InstIcmp::Ule: {
_sltu(DestT, Src1R, Src0R);
_br(TargetTrue, TargetFalse, DestT, CondMIPS32::Cond::NEZ);
break;
}
case InstIcmp::Sgt: {
_slt(DestT, Src1R, Src0R);
_br(TargetTrue, TargetFalse, DestT, CondMIPS32::Cond::EQZ);
break;
}
case InstIcmp::Sge: {
_slt(DestT, Src0R, Src1R);
_br(TargetTrue, TargetFalse, DestT, CondMIPS32::Cond::NEZ);
break;
}
case InstIcmp::Slt: {
_slt(DestT, Src0R, Src1R);
_br(TargetTrue, TargetFalse, DestT, CondMIPS32::Cond::EQZ);
break;
}
case InstIcmp::Sle: {
_slt(DestT, Src1R, Src0R);
_br(TargetTrue, TargetFalse, DestT, CondMIPS32::Cond::NEZ);
break;
}
}
}
}
void TargetMIPS32::lowerCall(const InstCall *Instr) {
......@@ -1112,7 +1190,7 @@ void TargetMIPS32::lowerIcmp(const InstIcmp *Instr) {
case InstIcmp::Sge: {
auto *DestT = I32Reg();
auto *T = I32Reg();
_slt(T, Src1R, Src0R);
_slt(T, Src0R, Src1R);
_xori(DestT, T, 1);
_mov(Dest, DestT);
return;
......@@ -1491,6 +1569,72 @@ Operand *TargetMIPS32::legalize(Operand *From, LegalMask Allowed,
return From;
}
namespace BoolFolding {
// TODO(sagar.thakur): Add remaining instruction kinds to shouldTrackProducer()
// and isValidConsumer()
bool shouldTrackProducer(const Inst &Instr) {
return Instr.getKind() == Inst::Icmp;
}
bool isValidConsumer(const Inst &Instr) { return Instr.getKind() == Inst::Br; }
} // end of namespace BoolFolding
void TargetMIPS32::ComputationTracker::recordProducers(CfgNode *Node) {
for (Inst &Instr : Node->getInsts()) {
if (Instr.isDeleted())
continue;
// Check whether Instr is a valid producer.
Variable *Dest = Instr.getDest();
if (Dest // only consider instructions with an actual dest var; and
&& Dest->getType() == IceType_i1 // only bool-type dest vars; and
&& BoolFolding::shouldTrackProducer(Instr)) { // white-listed instr.
KnownComputations.emplace(Dest->getIndex(),
ComputationEntry(&Instr, IceType_i1));
}
// Check each src variable against the map.
FOREACH_VAR_IN_INST(Var, Instr) {
SizeT VarNum = Var->getIndex();
auto ComputationIter = KnownComputations.find(VarNum);
if (ComputationIter == KnownComputations.end()) {
continue;
}
++ComputationIter->second.NumUses;
switch (ComputationIter->second.ComputationType) {
default:
KnownComputations.erase(VarNum);
continue;
case IceType_i1:
if (!BoolFolding::isValidConsumer(Instr)) {
KnownComputations.erase(VarNum);
continue;
}
break;
}
if (Instr.isLastUse(Var)) {
ComputationIter->second.IsLiveOut = false;
}
}
}
for (auto Iter = KnownComputations.begin(), End = KnownComputations.end();
Iter != End;) {
// Disable the folding if its dest may be live beyond this block.
if (Iter->second.IsLiveOut || Iter->second.NumUses > 1) {
Iter = KnownComputations.erase(Iter);
continue;
}
// Mark as "dead" rather than outright deleting. This is so that other
// peephole style optimizations during or before lowering have access to
// this instruction in undeleted form. See for example
// tryOptimizedCmpxchgCmpBr().
Iter->second.Instr->setDead();
++Iter;
}
}
TargetHeaderMIPS32::TargetHeaderMIPS32(GlobalContext *Ctx)
: TargetHeaderLowering(Ctx) {}
......
......@@ -46,6 +46,12 @@ public:
return makeUnique<MIPS32::AssemblerMIPS32>();
}
void initNodeForLowering(CfgNode *Node) override {
Computations.forgetProducers();
Computations.recordProducers(Node);
Computations.dump(Func);
}
void translateOm1() override;
void translateO2() override;
bool doBranchOpt(Inst *Instr, const CfgNode *NextNode) override;
......@@ -156,6 +162,17 @@ public:
void _br(CfgNode *Target) { Context.insert<InstMIPS32Br>(Target); }
void _br(CfgNode *TargetTrue, CfgNode *TargetFalse, Operand *Src0,
Operand *Src1, CondMIPS32::Cond Condition) {
Context.insert<InstMIPS32Br>(TargetTrue, TargetFalse, Src0, Src1,
Condition);
}
void _br(CfgNode *TargetTrue, CfgNode *TargetFalse, Operand *Src0,
CondMIPS32::Cond Condition) {
Context.insert<InstMIPS32Br>(TargetTrue, TargetFalse, Src0, Condition);
}
void _ret(Variable *RA, Variable *Src0 = nullptr) {
Context.insert<InstMIPS32Ret>(RA, Src0);
}
......@@ -387,6 +404,66 @@ protected:
private:
ENABLE_MAKE_UNIQUE;
class ComputationTracker {
public:
ComputationTracker() = default;
~ComputationTracker() = default;
void forgetProducers() { KnownComputations.clear(); }
void recordProducers(CfgNode *Node);
const Inst *getProducerOf(const Operand *Opnd) const {
auto *Var = llvm::dyn_cast<Variable>(Opnd);
if (Var == nullptr) {
return nullptr;
}
auto Iter = KnownComputations.find(Var->getIndex());
if (Iter == KnownComputations.end()) {
return nullptr;
}
return Iter->second.Instr;
}
void dump(const Cfg *Func) const {
if (!BuildDefs::dump() || !Func->isVerbose(IceV_Folding))
return;
OstreamLocker L(Func->getContext());
Ostream &Str = Func->getContext()->getStrDump();
Str << "foldable producer:\n";
for (const auto &Computation : KnownComputations) {
Str << " ";
Computation.second.Instr->dump(Func);
Str << "\n";
}
Str << "\n";
}
private:
class ComputationEntry {
public:
ComputationEntry(Inst *I, Type Ty) : Instr(I), ComputationType(Ty) {}
Inst *const Instr;
// Boolean folding is disabled for variables whose live range is multi
// block. We conservatively initialize IsLiveOut to true, and set it to
// false once we find the end of the live range for the variable defined
// by this instruction. If liveness analysis is not performed (e.g., in
// Om1 mode) IsLiveOut will never be set to false, and folding will be
// disabled.
bool IsLiveOut = true;
int32_t NumUses = 0;
Type ComputationType;
};
// ComputationMap maps a Variable number to a payload identifying which
// instruction defined it.
using ComputationMap = CfgUnorderedMap<SizeT, ComputationEntry>;
ComputationMap KnownComputations;
};
ComputationTracker Computations;
};
class TargetDataMIPS32 final : public TargetDataLowering {
......
; Tests for conditional branch instructions
; RUN: %if --need=allow_dump --need=target_MIPS32 --command %p2i \
; RUN: --filetype=asm --target mips32 -i %s --args -O2 --skip-unimplemented \
; RUN: -allow-externally-defined-symbols \
; RUN: | %if --need=allow_dump --need=target_MIPS32 --command FileCheck %s \
; RUN: --check-prefix=COMMON --check-prefix=MIPS32
; RUN: %if --need=allow_dump --need=target_MIPS32 --command %p2i \
; RUN: --filetype=asm --target mips32 -i %s --args -Om1 --skip-unimplemented \
; RUN: -allow-externally-defined-symbols \
; RUN: | %if --need=allow_dump --need=target_MIPS32 --command FileCheck %s \
; RUN: --check-prefix=COMMON --check-prefix=MIPS32-OM1
define internal i32 @cond_br_eq(i32 %arg1, i32 %arg2) {
entry:
%cmp1 = icmp eq i32 %arg1, %arg2
br i1 %cmp1, label %branch1, label %branch2
branch1:
ret i32 1
branch2:
ret i32 2
}
; COMMON-LABEL: cond_br_eq
; MIPS32: bne
; MIPS32-OM1: xor
; MIPS32-OM1: sltiu {{.*}}, {{.*}}, 1
; MIPS32-OM1: beqz
define internal i32 @cond_br_ne(i32 %arg1, i32 %arg2) {
entry:
%cmp1 = icmp ne i32 %arg1, %arg2
br i1 %cmp1, label %branch1, label %branch2
branch1:
ret i32 1
branch2:
ret i32 2
}
; COMMON-LABEL: cond_br_ne
; MIPS32: beq
; MIPS32-OM1: xor
; MIPS32-OM1: sltu {{.*}}, $zero, {{.*}}
; MIPS32-OM1: beqz
define internal i32 @cond_br_slt(i32 %arg1, i32 %arg2) {
entry:
%cmp1 = icmp slt i32 %arg1, %arg2
br i1 %cmp1, label %branch1, label %branch2
branch1:
ret i32 1
branch2:
ret i32 2
}
; COMMON-LABEL: cond_br_slt
; MIPS32: slt
; MIPS32: beqz
; MIPS32-OM1: slt
; MIPS32-OM1: beqz
define internal i32 @cond_br_sle(i32 %arg1, i32 %arg2) {
entry:
%cmp1 = icmp sle i32 %arg1, %arg2
br i1 %cmp1, label %branch1, label %branch2
branch1:
ret i32 1
branch2:
ret i32 2
}
; COMMON-LABEL: cond_br_sle
; MIPS32: slt
; MIPS32: bnez
; MIPS32-OM1: slt
; MIPS32-OM1: xori {{.*}}, {{.*}}, 1
; MIPS32-OM1: beqz
define internal i32 @cond_br_sgt(i32 %arg1, i32 %arg2) {
entry:
%cmp1 = icmp sgt i32 %arg1, %arg2
br i1 %cmp1, label %branch1, label %branch2
branch1:
ret i32 1
branch2:
ret i32 2
}
; COMMON-LABEL: cond_br_sgt
; MIPS32: slt
; MIPS32: beqz
; MIPS32-OM1: slt
; MIPS32-OM1: beqz
define internal i32 @cond_br_sge(i32 %arg1, i32 %arg2) {
entry:
%cmp1 = icmp sge i32 %arg1, %arg2
br i1 %cmp1, label %branch1, label %branch2
branch1:
ret i32 1
branch2:
ret i32 2
}
; COMMON-LABEL: cond_br_sge
; MIPS32: slt
; MIPS32: bnez
; MIPS32-OM1: slt
; MIPS32-OM1: xori {{.*}}, {{.*}}, 1
; MIPS32-OM1: beqz
define internal i32 @cond_br_ugt(i32 %arg1, i32 %arg2) {
entry:
%cmp1 = icmp ugt i32 %arg1, %arg2
br i1 %cmp1, label %branch1, label %branch2
branch1:
ret i32 1
branch2:
ret i32 2
}
; COMMON-LABEL: cond_br_ugt
; MIPS32: sltu
; MIPS32: beqz
; MIPS32-OM1: sltu
; MIPS32-OM1: beqz
define internal i32 @cond_br_uge(i32 %arg1, i32 %arg2) {
entry:
%cmp1 = icmp uge i32 %arg1, %arg2
br i1 %cmp1, label %branch1, label %branch2
branch1:
ret i32 1
branch2:
ret i32 2
}
; COMMON-LABEL: cond_br_uge
; MIPS32: sltu
; MIPS32: bnez
; MIPS32-OM1: sltu
; MIPS32-OM1: xori {{.*}}, {{.*}}, 1
; MIPS32-OM1: beqz
define internal i32 @cond_br_ult(i32 %arg1, i32 %arg2) {
entry:
%cmp1 = icmp ult i32 %arg1, %arg2
br i1 %cmp1, label %branch1, label %branch2
branch1:
ret i32 1
branch2:
ret i32 2
}
; COMMON-LABEL: cond_br_ult
; MIPS32: sltu
; MIPS32: beqz
; MIPS32-OM1: sltu
; MIPS32-OM1: beqz
define internal i32 @cond_br_ule(i32 %arg1, i32 %arg2) {
entry:
%cmp1 = icmp ule i32 %arg1, %arg2
br i1 %cmp1, label %branch1, label %branch2
branch1:
ret i32 1
branch2:
ret i32 2
}
; COMMON-LABEL: cond_br_ule
; MIPS32: sltu
; MIPS32: bnez
; MIPS32-OM1: sltu
; MIPS32-OM1: xori {{.*}}, {{.*}}, 1
; MIPS32-OM1: beqz
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