How to change the operand type in the instruction from 32bit to 64bit in LLVM? - c++

I am a newbie to LLVM. And I am trying to change the type of the loop variable (PHINode).
For example, I have an IR as follows:
for.cond1.preheader: ; preds = %entry, %for.inc15
%k.03 = phi i32 [ 0, %entry ], [ %inc16, %for.inc15 ]
br label %for.cond4.preheader
for.cond4.preheader: ; preds = %for.cond1.preheader, %for.inc12
%indvars.iv5 = phi i64 [ 0, %for.cond1.preheader ], [ %indvars.iv.next6, %for.inc12 ]
br label %for.body6
for.body6: ; preds = %for.cond4.preheader, %for.body6
......
%add7 = add nsw i32 %2, 1
......
for.inc12: ; preds = %for.body6
......
for.inc15: ; preds = %for.inc12
%inc16 = add nuw nsw i32 %k.03, 1
%exitcond9 = icmp ne i32 %inc16, 100
br i1 %exitcond9, label %for.cond1.preheader, label %for.end17, !llvm.loop !7 ā€‹
I want to change the type of the variable %k.03, hoping it will change from 32bit to 64bit. And recursively change all its references (Uses) to 64bit. The effect is as follows:
for.cond1.preheader: ; preds = %entry, %for.inc15
%k.03 = phi i64 [ 0, %entry ], [ %inc16, %for.inc15 ]
br label %for.cond4.preheader
for.cond4.preheader: ; preds = %for.cond1.preheader, %for.inc12
%indvars.iv5 = phi i64 [ 0, %for.cond1.preheader ], [ %indvars.iv.next6, %for.inc12 ]
br label %for.body6
for.body6: ; preds = %for.cond4.preheader, %for.body6
......
%add7 = add nsw i32 %2, 1
......
for.inc12: ; preds = %for.body6
......
for.inc15: ; preds = %for.inc12
%inc16 = add nuw nsw i64 %k.03, 1
%exitcond9 = icmp ne i64 %inc16, 100
br i1 %exitcond9, label %for.cond1.preheader, label %for.end17, !llvm.loop !7 ā€‹
Then my approach is as follows:
static void __ChangeInsTo64Bit(User *inst) {
if (!std::strcmp(cast<Instruction>(inst)->getOpcodeName(), "br")) {
return;
}
if (std::strcmp(cast<Instruction>(inst)->getOpcodeName(), "icmp")) {
inst->mutateType(inst->getType()->getWithNewBitWidth(64));
}
for (auto OI = inst->op_begin(), OE = inst->op_end(); OI != OE; ++OI) {
Value *val = *OI;
val->mutateType(val->getType()->getWithNewBitWidth(64));
LLVM_DEBUG(dbgs() << "The Operand is: " << val->getName()
<< "; The bitwidth is: " << val->getType()->getIntegerBitWidth() << "\n");
}
}
static void __ChangePHINodeWidthTo64(Loop *OuterLoop, ScalarEvolution *SE) {
PHINode *OuterPHINode = OuterLoop->getInductionVariable(*SE);
if (!OuterPHINode) {
return ;
}
unsigned int outerPHIWidth = OuterPHINode->getType()->getIntegerBitWidth();
if (outerPHIWidth == 32) {
__ChangeInsTo64Bit(OuterPHINode);
for (User* user : OuterPHINode->users()) {
__ChangeInsTo64Bit(user);
for (User* u : user->users()) {
__ChangeInsTo64Bit(u);
}
}
}
}
Through the above code, I can achieve my goal, but weirdly modify the data type in the basic block for.body6:
%add7 = add nsw i32 %2, i64 1
This will cause program errors. Can someone help me point out the wrong point in my approach or provide the correct approach?

if (!std::strcmp(cast<Instruction>(inst)->getOpcodeName(), "br")) {
return;
}
if (std::strcmp(cast<Instruction>(inst)->getOpcodeName(), "icmp")) {
inst->mutateType(inst->getType()->getWithNewBitWidth(64));
}
That seems like a very janky and error-prone way to do RTTI, especially since LLVM provides the isa<> template for that very purpose.
Case in point, the "icmp" check will match everything that is not icmp (since there is no ! in front of the condition), and I would expect that this is not what you intented.
At the very least, the following is equivalent to your code and a lot more legible:
if (isa<BranchInst>(inst)) {
return;
}
if (!isa<ICmpInst>(inst)) {
inst->mutateType(inst->getType()->getWithNewBitWidth(64));
}
Removing the negation in front of the second test should address at least part of your issue.

Related

LLVM LoopPass values used outside the loop

I'm writing an LLVM LoopPass in which I need to know which values
are used outside the loop. For that I have this code:
virtual bool runOnLoop(Loop *loop, LPPassManager &LPM)
{
for (auto it = loop->block_begin(); it != loop->block_end(); it++)
{
for (auto inst = (*it)->begin(); inst != (*it)->end(); inst++)
{
if (Is_Used_Outside_This_loop(loop,(Instruction *) inst))
{
errs() << inst->getName().str();
errs() << " is used outside the loop\n";
}
}
}
// ...
}
The inner function seemed right at first, but with the *.ll file below,
it gives incorrect classification for %tmp5 since it is used twice
inside a basic block of the loop.
bool Is_Used_Outside_This_loop(Loop *loop, Value *v)
{
int n=0;
int numUses = v->getNumUses();
for (auto it = loop->block_begin(); it != loop->block_end(); it++)
{
if (v->isUsedInBasicBlock(*it))
{
n++;
}
}
if (n == numUses) return false;
else return true;
}
The following *.ll code shows that %tmp5 is used twice
inside a basic block of the loop. When I carefully searched the API,
I couldn't find anything like Value::numUsesInBasicBlock( ... )
; Function Attrs: nounwind uwtable
define internal void #foo(i8* %s) #0 {
entry:
%s.addr = alloca i8*, align 8
%c = alloca i8, align 1
store i8* %s, i8** %s.addr, align 8
store i8 0, i8* %c, align 1
br label %while.cond
while.cond: ; preds = %while.body, %entry
%tmp = load i8*, i8** %s.addr, align 8
%tmp1 = load i8, i8* %tmp, align 1
%conv = sext i8 %tmp1 to i32
%cmp = icmp eq i32 %conv, 97
br i1 %cmp, label %lor.end, label %lor.rhs
lor.rhs: ; preds = %while.cond
%tmp2 = load i8*, i8** %s.addr, align 8
%tmp3 = load i8, i8* %tmp2, align 1
%conv2 = sext i8 %tmp3 to i32
%cmp3 = icmp eq i32 %conv2, 98
br label %lor.end
lor.end:; preds = %lor.rhs, %while.cond
%tmp4 = phi i1 [ true, %while.cond ], [ %cmp3, %lor.rhs ]
br i1 %tmp4, label %while.body, label %while.end
while.body: ; preds = %lor.end
%tmp5 = load i8*, i8** %s.addr, align 8
%incdec.ptr = getelementptr inbounds i8, i8* %tmp5, i32 1
store i8* %incdec.ptr, i8** %s.addr, align 8
%tmp6 = load i8, i8* %tmp5, align 1
store i8 %tmp6, i8* %c, align 1
br label %while.cond
while.end: ; preds = %lor.end
%tmp7 = load i8*, i8** %s.addr, align 8
%tmp8 = load i8, i8* %tmp7, align 1
%conv5 = sext i8 %tmp8 to i32
%cmp6 = icmp eq i32 %conv5, 99
br i1 %cmp6, label %if.then, label %if.end
if.then: ; preds = %while.end
%tmp9 = load i8*, i8** %s.addr, align 8
%incdec.ptr8 = getelementptr inbounds i8, i8* %tmp9, i32 1
store i8* %incdec.ptr8, i8** %s.addr, align 8
br label %if.end
if.end: ; preds = %if.then, %while.end
ret void
}
Clearly, there's got to be a way of doing this, right? Thanks!
The problem is your exit condition. You ask for uses, but then compare with the number of blocks that the value is used in.
So, if the value is used twice in the same basic block, the number of uses is 2 and the n counter of basic blocks that used the value is only incremented once, hence the mismatch of n and numUses.
Maybe a more concise way to do what you want is:
void FindUsesNotIn(
llvm::SmallPtrSetImpl<llvm::BasicBlock *> &Blocks,
llvm::SmallPtrSetImpl<llvm::Value *> &OutUses) {
for(const auto &b : Blocks)
for(auto &i : *b)
for(const auto &u : i.users()) {
auto *userInst = llvm::dyn_cast<llvm::Instruction>(u);
if(userInst && !Blocks.count(userInst->getParent())) {
OutUses.insert(&i);
break;
}
}
}
and then in the runOnLoop method have something like this:
virtual bool runOnLoop(llvm::Loop *loop, llvm::LPPassManager &LPM) {
llvm::SmallPtrSet<llvm::BasicBlock*, 10> loopBlocks(loop->block_begin(), loop->block_end());
llvm::SmallPtrSet<llvm::Value *, 10> outs;
FindUsesNotIn(loopBlocks, outs);
for(const auto *e : outs)
llvm::dbgs() << *e << '\n';
return false;
}
Here's how I solved it, though it looks overly complicated:
virtual bool runOnLoop(Loop *loop, LPPassManager &LPM)
{
for (auto it = loop->block_begin(); it != loop->block_end(); it++)
{
for (auto inst = (*it)->begin(); inst != (*it)->end(); inst++)
{
int n=0;
for (auto use = inst->use_begin(); use != inst->use_end(); use++)
{
Instruction *i = (Instruction *) use->getUser();
if (BasicBlockBelongsToLoop(i->getParent(),loop))
{
n++;
}
}
assert(n <= inst->getNumUses());
if (n < inst->getNumUses())
{
errs() << inst->getName().str();
errs() << " is used outside the loop\n";
}
}
}
// ...
I also couldn't found in the API, how to check if a basic block belongs to a loop,
so I had to write my own BasicBlockBelongsToLoop, here it is:
bool BasicBlockBelongsToLoop(BasicBlock *BB, Loop *loop)
{
for (auto it = loop->block_begin(); it != loop->block_end(); it++)
{
if (BB == (*it))
{
return true;
}
}
return false;
}

How to get Instruction in MachineInstr?

I wanted to know variable dependence in a real register (like X86:EAX, EBX ...). So, I have created an IR-PASS that can identify dependencies on the IR. This pass uses the newly added variables unsigned HasDependency: 1; and unsigned HasMaybeDependency: 1; in the Value class.
.
.
// Use the same type as the bitfield above so that MSVC will pack them.
unsigned IsUsedByMD : 1;
unsigned HasName : 1;
unsigned HasHungOffUses : 1;
unsigned HasDescriptor : 1;
unsigned HasDependency : 1;
unsigned HasMaybeDependency : 1;
.
.
.
void setDependency() { HasDependency = true; }
void setMaybeDependency() { HasMaybeDependency = true; }
bool hasDependency() const { return HasDependency; }
bool hasMaybeDependency() const { return HasMaybeDependency; }
//static_assert(sizeof(Value) == 2 * sizeof(void *) + 2 * sizeof(unsigned),
// "Value too big");
When applied to a code snippet like this:
extern int foo_called(int a);
int foo(int k)
{
int __attribute__((annotate("xxx"))) a;
for (int i = 0; i < k; i++)
{
int c = a + k;
a += foo_called(c);
}
return 0;
}
which produces this bitcode:
define i32 #"\01?foo##YAHH#Z"(i32 %k) local_unnamed_addr #0 {
entry:
%a = alloca i32, align 4
%0 = bitcast i32* %a to i8*
call void #llvm.lifetime.start.p0i8(i64 4, i8* nonnull %0) #2
call void #llvm.var.annotation(i8* nonnull %0, i8* getelementptr inbounds ([4 x i8], [4 x i8]* #.str, i32 0, i32 0), i8* getelementptr inbounds ([6 x i8], [6 x i8]* #.str.1, i32 0, i32 0), i32 17)
%cmp7 = icmp sgt i32 %k, 0
br i1 %cmp7, label %for.body.lr.ph, label %for.cond.cleanup
for.body.lr.ph: ; preds = %entry
%.pre = load i32, i32* %a, align 4, !tbaa !3
br label %for.body
for.cond.cleanup: ; preds = %for.body, %entry
call void #llvm.lifetime.end.p0i8(i64 4, i8* nonnull %0) #2
ret i32 0
for.body: ; preds = %for.body, %for.body.lr.ph
%1 = phi i32 [ %.pre, %for.body.lr.ph ], [ %add2, %for.body ]
%i.08 = phi i32 [ 0, %for.body.lr.ph ], [ %inc, %for.body ]
%add = add nsw i32 %1, %k
%call = call i32 #"\01?foo_called##YAHH#Z"(i32 %add)
%2 = load i32, i32* %a, align 4, !tbaa !3
%add2 = add nsw i32 %2, %call
store i32 %add2, i32* %a, align 4, !tbaa !3
%inc = add nuw nsw i32 %i.08, 1
%exitcond = icmp eq i32 %inc, %k
br i1 %exitcond, label %for.cond.cleanup, label %for.body
}
declare i32 #"\01?foo_called##YAHH#Z"(i32) local_unnamed_addr #3
The result of the the pass on the above bitcode is:
Function - ?foo##YAHH#Z
Annotated Variable List :
- Annotated : a(message: xxx)
Annotated-Variable : a
(Perpect) %add2 = add nsw i32 %2, %call
(Perpect) %2 = load i32, i32* %a, align 4, !tbaa !3
(Perpect) %a = alloca i32, align 4
(Perpect) %cmp7 = icmp sgt i32 %k, 0
(Maybe) %exitcond = icmp eq i32 %inc, %k
(Maybe) %inc = add nuw nsw i32 %i.08, 1
(Maybe) %i.08 = phi i32 [ 0, %for.body.lr.ph ], [ %inc, %for.body ]
(Perpect) %call = call i32 #"\01?foo_called##YAHH#Z"(i32 %add)
(Perpect) %add = add nsw i32 %1, %k
(Perpect) %1 = phi i32 [ %.pre, %for.body.lr.ph ], [ %add2, %for.body ]
(Perpect) %.pre = load i32, i32* %a, align 4, !tbaa !3
I followed the SelectionDAGISel.cpp: SelectAllBasicBlocks function to get information from the backend, but I was able to get only AllocaInst, StoreInst, and LoadInst using as follows:
for (MachineBasicBlock &MBB : mf) {
for (MachineInstr& I : MBB) {
for (MachineInstr::mmo_iterator i = I.memoperands_begin(),
e = I.memoperands_end();
i != e; ++i) {
if (const Value *V = (*i)->getValue())
errs() << *V << "\n";
}
}
}
How do I know the correlation between MachineInstr and Instruction? If it is not provided in LLVM, which parts need to be fixed?
This is not normal. This is an trick. But I am using this method very usefully. If you know the normal way, please give me a comment.
I solved this problem using DebugLoc. It is used to represent the line-column-row, function-name etc... information of .c, .cpp files. This information will remain from the time of ;;vm-ir until MachineInstr.
So, if it is guaranteed that DebugLoc is not used in your compiler processing, you can put the address of the class that contains the information needed for the row information. This will allow you to cast the DebugLoc row to the desired class at the right time. (You can use column, because column must less than 2^16.)
The following describes in detail the method I used.
Change file and Re-Build your project.
Several design patterns were used to maximize memory efficiency, so I could not easily change the class.
First, modify DebugLoc-print routine. GOTO DebugLoc.cpp and delete DIScope print routine like this. This processing save you form runtime-error.
void DebugLoc::print(raw_ostream &OS) const {
if (!Loc)
return;
// Print source line info.
//auto *Scope = cast<DIScope>(getScope());
//OS << Scope->getFilename();
OS << ':' << getLine();
if (getCol() != 0)
OS << ':' << getCol();
Second, The verifier should be modified. This syntax will be helpful.
void Verifier::visitDILocation(const DILocation &N) {
- AssertDI(N.getRawScope() && isa<DILocalScope>(N.getRawScope()),
- "location requires a valid scope", &N, N.getRawScope());
+ //AssertDI(N.getRawScope() && isa<DILocalScope>(N.getRawScope()),
+ // "location requires a valid scope", &N, N.getRawScope());
if (auto *IA = N.getRawInlinedAt())
AssertDI(isa<DILocation>(IA), "inlined-at should be a location", &N, IA);
}
Third, there are some formal steps to register a class in DebugLoc. Create initialize function for this.
static LLVMContext cnt;
static MDNode *md;
md = MDNode::get(cnt, DILocation::get(cnt, 100, 100, DIScope::get(cnt, nullptr)));
Last, create register function.
static DebugLoc getDebugLoc(DependencyInstrInfoManager *info)
{
return DebugLoc::get(reinterpret_cast<unsigned> (info), (uint16_t)-1, md);
}
static void setDebugLoc(Instruction *I, ...)
{
DependencyInstrInfoManager *mgr;
if (I->getDebugLoc()) {
mgr = reinterpret_cast<DependencyInstrInfoManager *>
(I->getDebugLoc()->getLine());
} else {
mgr = new DependencyInstrInfoManager();
I->setDebugLoc(getDebugLoc(mgr));
}
mgr->addInfo(new DependencyInstrInfo(I, S, T, ...));
}
DependencyInstrInfoManager is the class for answering the above questions.
Finally, you can print your own information in XXXMCInstLower.cpp:EmitInstruction();(like X86MCInstLower.cpp). The following statement is an example of the output of my case.
void X86AsmPrinter::EmitInstruction(const MachineInstr *MI) {
X86MCInstLower MCInstLowering(*MF, *this);
const X86RegisterInfo *RI = MF->getSubtarget<X86Subtarget>().getRegisterInfo();
if (MI->getDebugLoc()) {
DependencyInstrInfoManager *mgr = reinterpret_cast<DependencyInstrInfoManager *>
(MI->getDebugLoc()->getLine());
mgr->doFolding();
for (auto DI : *mgr)
OutStreamer->AddComment(DI->getInfo());
}
Dependency Marking
I have done dependency marking using this method.
int foo(int k)
{
int ANNORATE("b") b = 0;
int ANNORATE("a") a = 0;
for (int i = 0; i < k; i++)
{
int c = a + k;
int d = b + k;
a += foo_called(c);
b += foo_called2(c);
}
return a + foo_called(b);
}
to
# BB#1: # %for.body.preheader
movl %esi, %ebx
.p2align 4, 0x90
LBB0_2 : # %for.body
# =>This Inner Loop Header: Depth=1
addl %esi, %edi # [Perpect, Source:b]
# [Perpect, Source: a]
pushl %edi # [Maybe, Source:b]
# [Perpect, Source: a]
calll "?foo_called##YAHH#Z" # [Maybe, Source:b]
# [Perpect, Source: a]
addl $4, %esp # [Maybe, Source:b]
# [Perpect, Source: a]
addl %eax, 4(%esp)
pushl %edi # [Perpect, Source:b]
calll "?foo_called2##YAHH#Z" # [Perpect, Source:b]
addl $4, %esp # [Perpect, Source:b]
addl(%esp), %eax # [Annotated, Source:b]
movl 4(%esp), %edi # [Perpect, Source:b]
# [Perpect, Source: a]
decl %ebx # [Maybe, Source:b]
movl %eax, (%esp)
jne LBB0_2
jmp LBB0_4

LLVM IR position of predecessor block labels in phi node instruction

When using a phi node in a basic block is there a suggested order in which I should place the labels if there is a higher probability that the predecessor is a certain block. For example take the simple factorial function listed below.
define private i64 #fact(i64 %start) {
entry:
%0 = icmp sle i64 1, %start
br i1 %0, label %loop, label %endcond
loop: ; preds = %loop, %entry
%1 = phi i64 [ %res, %loop ], [ 1, %entry ] ; if %start > 2 predecessor
%2 = phi i64 [ %3, %loop ], [ %start, %entry ] ; is likely %loop
%res = mul i64 %1, %2
%3 = sub i64 %2, 1
%cond = icmp sle i64 1, %3
br i1 %cond, label %loop, label %endcond
endcond: ; preds = %loop, %entry
%fin = phi i64 [ %res, %loop ], [ 1, %entry ] ; highly unlikely
ret i64 %fin ; predecessor is %entry
}
While it is possible that the user will input #fact(1) it is unlikely, so I expect in most cases the predecessor block for the phi node in endcond to be post.loop. So is my assumption that in this case
%fin = phi i64 [ %res, %post.loop ], [ 1, %entry ]
is better than
%fin = phi i64 [ 1, %entry ], [ %res, %post.loop ]
correct? And if so, why or why not?
Doesn't make a difference. LLVM will do an analysis on your code to estimate the branch probabilities, and it uses that to order the resulting block.
You can influence this by using branch weight metadata: http://llvm.org/docs/BlockFrequencyTerminology.html

LLVM "Instruction does not dominate all uses" - Inserting new Instruction

I am getting the following error while inserting an instruction using an llvm pass:
Instruction does not dominate all uses!
%add = add nsw i32 10, 2
%cmp3 = icmp ne i32 %a.01, %add
Broken module found, compilation aborted!
I have the source code in a bitcode file whose snippet is:
if.then: ; preds = %entry
%add = add nsw i32 10, 2
br label %if.end
if.else: ; preds = %entry
%sub = sub nsw i32 10, 2
br label %if.end
if.end: ; preds = %if.else, %if.then
%a.0 = phi i32 [ %add, %if.then ], [ %sub, %if.else ]
%a.01 = call i32 #tauInt32Ty(i32 %a.0) ; line A
%add3 = add nsw i32 %a.01, 2
%add4 = add nsw i32 %a.01, 3
%call5 = call i32 (i8*, ...)* #printf(i8* getelementptr inbounds ([7 x i8]* #.str2, i32 0, i32 0), i32 %add3, i32 %add4)
I want to insert a new instruction after "line A" which is :
%cmp3 = icmp ne i32 %a.01, %add
And I have written a function pass whose snippet of the code which does this task is :
for (Function::iterator bb = F.begin(), e = F.end(); bb != e; ++bb) {
for (BasicBlock::iterator i = bb->begin(), e = bb->end(); i != e; ++i) {
std::string str;
if(isa<CallInst>(i))// || true) {
BasicBlock::iterator next_it = i;
next_it++;
Instruction* next = dyn_cast<Instruction>(&*next_it);
CallInst* ci = dyn_cast<CallInst>(&*i);
Function* ff = ci->getCalledFunction();
str = ff->getName();
errs()<<"> "<<str<<"\n";
if(!str.compare("tauInt32Ty")) {
hotPathSSA1::varVersionWithPathsSet::iterator start = tauArguments[&*ci].begin();
hotPathSSA1::varVersionWithPathsSet::iterator end = tauArguments[&*ci].end();
Value* specArgs = start->second; // specArgs points to %add
ICmpInst* int1_cmp_56 = new ICmpInst(next, ICmpInst::ICMP_NE, ci, specArgs, "cmp3");
}
}
}
}
I have not encountered such a problem jet but I think your problem is the if statement. %add belonges to the if.then BasicBlock and it is not accessable from the if.end block. This is why the phi instruction "chooses" which value is available %add or %sub. So you have to take %a.0 for your IcmpInst as argument not %add.

nested if vs loop condition

I have to do a comparison and I want to know which will be faster.
1)
for (i=0;i<4;i++){
if (object1(i)==object2(i))
retval = true;
else {
retval = false;
break;
}
}
2)
if ( (object1(0)==object2(0) && (object1(1)==object2(1) && (object1(2)==object2(2) && (object1(3)==object2(3)){
retval = true;
else
retval = false;
Or both will perform the same?
Thanks for Advice
Strictly speaking the most efficient path would be:
retval = object1(0) == object2(0) && object1(1) == object2(1).....
This basically does the same as your loop, but doesn't have to compare the result to true to determine the outcome of the condition.
However, I strongly recommend keeping the loop, as it is far easier to adapt to add or remove numbers.
You need to measure. But in any case the first code can be simplified quite a bit:
for (i = 0; i < 4; ++i)
if (object1(i) != object2(i))
return false;
return true;
Now choose the more readable form. Iā€™d choose the loop here, unless you have confirmed that there is a performance problem caused by this code.
If the optimization flags are on, then the compiler might produce same machine instructtions for both code, unlooping the for loop completely, as the exact number of iteration is known to the compiler:
loop unrolling
By the way, if you care so much, then you could write this:
bool retValue = (object1(0)==object2(0)) &&
(object1(1)==object2(1)) &&
(object1(2)==object2(2)) &&
(object1(3)==object2(3));
which avoids both: for loop, as well as if-else branch, and it doesn't depend on compiler optimization.
As always with optimization, the one and single rule is MEASURE.
Furthermore, I guess that the compiler could optimize this code in some ways you (and I) couldn't even imagine. Therefore I'd suggest to write it in the most readable form.
I like to play with the Try out LLVM and Clang page for this:
struct Object {
int operator()(int i) const;
};
bool loop(Object const& left, Object const& right) {
bool retval = false;
for (int i = 0; i < 4; i++) {
if (left(i) == right(i) )
retval = true;
else {
retval = false;
break;
}
}
return true;
}
bool inlineif(Object const& left, Object const& right) {
bool retval = true;
if ( left(0) == right(0) &&
left(1) == right(1) &&
left(2) == right(2) &&
left(3) == right(3))
retval = true;
else
retval = false;
return retval;
}
bool betterloop(Object const& left, Object const& right) {
for (int i = 0; i < 4; ++i)
if (left(i) != right(i))
return false;
return true;
}
bool betterif(Object const& left, Object const& right) {
return left(0) == right(0) &&
left(1) == right(1) &&
left(2) == right(2) &&
left(3) == right(3);
}
Produces the following IR for loops (regardless of how they are written):
define zeroext i1 #_Z4loopRK6ObjectS1_(%struct.Object* %left, %struct.Object* %right) uwtable {
br label %1
; <label>:1 ; preds = %7, %0
%i.0 = phi i32 [ 0, %0 ], [ %8, %7 ]
%2 = icmp slt i32 %i.0, 4
br i1 %2, label %3, label %9
; <label>:3 ; preds = %1
%4 = tail call i32 #_ZNK6ObjectclEi(%struct.Object* %left, i32 %i.0)
%5 = tail call i32 #_ZNK6ObjectclEi(%struct.Object* %right, i32 %i.0)
%6 = icmp eq i32 %4, %5
br i1 %6, label %7, label %9
; <label>:7 ; preds = %3
%8 = add nsw i32 %i.0, 1
br label %1
; <label>:9 ; preds = %3, %1
ret i1 true
}
And a very similar IR for the two if (so I'll give only one):
define zeroext i1 #_Z8betterifRK6ObjectS1_(%struct.Object* %left, %struct.Object* %right) uwtable {
%1 = tail call i32 #_ZNK6ObjectclEi(%struct.Object* %left, i32 0)
%2 = tail call i32 #_ZNK6ObjectclEi(%struct.Object* %right, i32 0)
%3 = icmp eq i32 %1, %2
br i1 %3, label %4, label %16
; <label>:4 ; preds = %0
%5 = tail call i32 #_ZNK6ObjectclEi(%struct.Object* %left, i32 1)
%6 = tail call i32 #_ZNK6ObjectclEi(%struct.Object* %right, i32 1)
%7 = icmp eq i32 %5, %6
br i1 %7, label %8, label %16
; <label>:8 ; preds = %4
%9 = tail call i32 #_ZNK6ObjectclEi(%struct.Object* %left, i32 2)
%10 = tail call i32 #_ZNK6ObjectclEi(%struct.Object* %right, i32 2)
%11 = icmp eq i32 %9, %10
br i1 %11, label %12, label %16
; <label>:12 ; preds = %8
%13 = tail call i32 #_ZNK6ObjectclEi(%struct.Object* %left, i32 3)
%14 = tail call i32 #_ZNK6ObjectclEi(%struct.Object* %right, i32 3)
%15 = icmp eq i32 %13, %14
br label %16
; <label>:16 ; preds = %12, %8, %4, %0
%17 = phi i1 [ false, %8 ], [ false, %4 ], [ false, %0 ], [ %15, %12 ]
ret i1 %17
}
The important instructions here is br which is the branching instruction. It can be used either as a simple goto or with conditions on the edges:
br i1 %11, label %12, label %16
means if i1 is true, go to label %12, otherwise go to label %16.
It seems that "naturally" LLVM will not unroll the traditional loop version, so the if version performs better here. I am quite surprised, actually, that it does not and I cannot figure out why it would not...
So, the inline if code might be a bit faster, but it might also be unnoticeable depending on the cost of left(i) == right(i) (and even then), as CPU are quite good at branch prediction.