Мне нравится играть со страницей Try LLVM и Clang:
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);
}
Создает следующий IR для циклов (независимо от того, как они записаны):
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
}
И очень похожий ИК для двух, если (поэтому я дам только один):
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
}
Важные инструкции здесь br
, которые являются инструкциями ветвления. Может использоваться как простой goto
или с условиями на краях:
br i1 %11, label %12, label %16
означает, что если i1
истинно, перейдите к label %12
, в противном случае перейдите к label %16
.
Кажется, что "естественно" LLVM не развернет традиционную версию цикла, поэтому версия if
работает лучше. На самом деле я весьма удивлен, что это не так, и я не могу понять, почему это не ...
Таким образом, встроенный код может быть немного быстрее, но он также может быть незаметным в зависимости от стоимости left(i) == right(i)
(и даже тогда), поскольку ЦП достаточно хороши для прогнозирования ветвлений.