277 changes: 277 additions & 0 deletions llvm/test/Transforms/SimplifyCFG/hoist-common-skip.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,277 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -S --passes='simplifycfg<hoist-common-insts>' %s | FileCheck %s

;; Check that the two loads are hoisted to the common predecessor, skipping
;; over the add/sub instructions.

define void @f0(i1 %c, ptr nocapture noundef %d, ptr nocapture noundef readonly %m, ptr nocapture noundef readonly %b) {
; CHECK-LABEL: @f0(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
; CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[M:%.*]], align 2
; CHECK-NEXT: br i1 [[C:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CHECK: if.then:
; CHECK-NEXT: [[ADD:%.*]] = add nsw i16 [[TMP0]], 1
; CHECK-NEXT: [[U:%.*]] = add i16 [[ADD]], [[TMP1]]
; CHECK-NEXT: br label [[IF_END:%.*]]
; CHECK: if.else:
; CHECK-NEXT: [[SUB:%.*]] = sub nsw i16 [[TMP0]], 1
; CHECK-NEXT: [[TMP2:%.*]] = add i16 [[SUB]], 3
; CHECK-NEXT: [[V:%.*]] = add i16 [[SUB]], [[TMP2]]
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: [[UV:%.*]] = phi i16 [ [[V]], [[IF_ELSE]] ], [ [[U]], [[IF_THEN]] ]
; CHECK-NEXT: store i16 [[UV]], ptr [[D:%.*]], align 2
; CHECK-NEXT: ret void
;
entry:
br i1 %c, label %if.then, label %if.else

if.then:
%0 = load i16, ptr %b, align 2
%add = add nsw i16 %0, 1
%1 = load i16, ptr %m, align 2
%u = add i16 %add, %1
br label %if.end

if.else:
%2 = load i16, ptr %b, align 2
%sub = sub nsw i16 %2, 1
%3 = load i16, ptr %m, align 2
%4 = add i16 %sub, 3
%v = add i16 %sub, %4
br label %if.end

if.end:
%uv = phi i16 [ %v, %if.else ], [ %u, %if.then ]
store i16 %uv, ptr %d, align 2
ret void
}


;; Check some instructions (e.g. add) can be reordered across instructions with side
;; effects, while others (e.g. load) can't.
define void @f2(i1 %c, ptr nocapture noundef %d, ptr nocapture noundef readonly %m, ptr nocapture noundef readonly %b) {
; CHECK-LABEL: @f2(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
; CHECK-NEXT: [[ADD_0:%.*]] = add nsw i16 [[TMP0]], 1
; CHECK-NEXT: br i1 [[C:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CHECK: if.then:
; CHECK-NEXT: call void @side_effects0()
; CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[M:%.*]], align 2
; CHECK-NEXT: [[U:%.*]] = add i16 [[ADD_0]], [[TMP1]]
; CHECK-NEXT: br label [[IF_END:%.*]]
; CHECK: if.else:
; CHECK-NEXT: call void @no_side_effects0()
; CHECK-NEXT: [[TMP2:%.*]] = load i16, ptr [[M]], align 2
; CHECK-NEXT: [[V:%.*]] = add i16 [[ADD_0]], [[TMP2]]
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: [[UV:%.*]] = phi i16 [ [[V]], [[IF_ELSE]] ], [ [[U]], [[IF_THEN]] ]
; CHECK-NEXT: store i16 [[UV]], ptr [[D:%.*]], align 2
; CHECK-NEXT: ret void
;
entry:
br i1 %c, label %if.then, label %if.else

if.then:
%0 = load i16, ptr %b, align 2
call void @side_effects0()
%add.0 = add nsw i16 %0, 1
%1 = load i16, ptr %m, align 2
%u = add i16 %add.0, %1
br label %if.end

if.else:
%2 = load i16, ptr %b, align 2
call void @no_side_effects0()
%add.1 = add nsw i16 %2, 1
%3 = load i16, ptr %m, align 2
%v = add i16 %add.1, %3
br label %if.end

if.end:
%uv = phi i16 [ %v, %if.else ], [ %u, %if.then ]
store i16 %uv, ptr %d, align 2
ret void
}


;; Check indeed it was the side effects that prevented hoisting the load
;; in the previous test.
define void @f3(i1 %c, ptr nocapture noundef %d, ptr nocapture noundef readonly %m, ptr nocapture noundef readonly %b) {
; CHECK-LABEL: @f3(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
; CHECK-NEXT: [[ADD_0:%.*]] = add nsw i16 [[TMP0]], 1
; CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[M:%.*]], align 2
; CHECK-NEXT: [[U:%.*]] = add i16 [[ADD_0]], [[TMP1]]
; CHECK-NEXT: br i1 [[C:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CHECK: if.then:
; CHECK-NEXT: call void @no_side_effects0()
; CHECK-NEXT: br label [[IF_END:%.*]]
; CHECK: if.else:
; CHECK-NEXT: call void @no_side_effects1()
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: store i16 [[U]], ptr [[D:%.*]], align 2
; CHECK-NEXT: ret void
;
entry:
br i1 %c, label %if.then, label %if.else

if.then:
%0 = load i16, ptr %b, align 2
call void @no_side_effects0()
%add.0 = add nsw i16 %0, 1
%1 = load i16, ptr %m, align 2
%u = add i16 %add.0, %1
br label %if.end

if.else:
%2 = load i16, ptr %b, align 2
call void @no_side_effects1()
%add.1 = add nsw i16 %2, 1
%3 = load i16, ptr %m, align 2
%v = add i16 %add.1, %3
br label %if.end

if.end:
%uv = phi i16 [ %v, %if.else ], [ %u, %if.then ]
store i16 %uv, ptr %d, align 2
ret void
}

;; Check some instructions (e.g. sdiv) are not speculatively executed.

;; Division by non-zero constant OK to speculate ...
define void @f4(i1 %c, ptr nocapture noundef %d, ptr nocapture noundef readonly %m, ptr nocapture noundef readonly %b) {
; CHECK-LABEL: @f4(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
; CHECK-NEXT: [[DIV_0:%.*]] = sdiv i16 [[TMP0]], 2
; CHECK-NEXT: [[U:%.*]] = add i16 [[DIV_0]], [[TMP0]]
; CHECK-NEXT: br i1 [[C:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CHECK: if.then:
; CHECK-NEXT: call void @side_effects0()
; CHECK-NEXT: br label [[IF_END:%.*]]
; CHECK: if.else:
; CHECK-NEXT: call void @side_effects1()
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: store i16 [[U]], ptr [[D:%.*]], align 2
; CHECK-NEXT: ret void
;
entry:
br i1 %c, label %if.then, label %if.else

if.then:
%0 = load i16, ptr %b, align 2
call void @side_effects0()
%div.0 = sdiv i16 %0, 2
%u = add i16 %div.0, %0
br label %if.end

if.else:
%1 = load i16, ptr %b, align 2
call void @side_effects1()
%div.1 = sdiv i16 %1, 2
%v = add i16 %div.1, %1
br label %if.end

if.end:
%uv = phi i16 [ %v, %if.else ], [ %u, %if.then ]
store i16 %uv, ptr %d, align 2
ret void
}

;; ... but not a general division ...
define void @f5(i1 %c, ptr nocapture noundef %d, ptr nocapture noundef readonly %m, ptr nocapture noundef readonly %b) {
; CHECK-LABEL: @f5(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
; CHECK-NEXT: br i1 [[C:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CHECK: if.then:
; CHECK-NEXT: call void @side_effects0()
; CHECK-NEXT: [[DIV_0:%.*]] = sdiv i16 211, [[TMP0]]
; CHECK-NEXT: [[U:%.*]] = add i16 [[DIV_0]], [[TMP0]]
; CHECK-NEXT: br label [[IF_END:%.*]]
; CHECK: if.else:
; CHECK-NEXT: call void @side_effects1()
; CHECK-NEXT: [[DIV_1:%.*]] = sdiv i16 211, [[TMP0]]
; CHECK-NEXT: [[V:%.*]] = add i16 [[DIV_1]], [[TMP0]]
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: [[UV:%.*]] = phi i16 [ [[V]], [[IF_ELSE]] ], [ [[U]], [[IF_THEN]] ]
; CHECK-NEXT: store i16 [[UV]], ptr [[D:%.*]], align 2
; CHECK-NEXT: ret void
;
entry:
br i1 %c, label %if.then, label %if.else

if.then:
%0 = load i16, ptr %b, align 2
call void @side_effects0()
%div.0 = sdiv i16 211, %0
%u = add i16 %div.0, %0
br label %if.end

if.else:
%1 = load i16, ptr %b, align 2
call void @side_effects1()
%div.1 = sdiv i16 211, %1
%v = add i16 %div.1, %1
br label %if.end

if.end:
%uv = phi i16 [ %v, %if.else ], [ %u, %if.then ]
store i16 %uv, ptr %d, align 2
ret void
}

;; ... and it's also OK to hoist the division when there's no speculation happening.
define void @f6(i1 %c, ptr nocapture noundef %d, ptr nocapture noundef readonly %m, ptr nocapture noundef readonly %b) {
; CHECK-LABEL: @f6(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
; CHECK-NEXT: [[DIV_0:%.*]] = sdiv i16 211, [[TMP0]]
; CHECK-NEXT: [[U:%.*]] = add i16 [[DIV_0]], [[TMP0]]
; CHECK-NEXT: br i1 [[C:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CHECK: if.then:
; CHECK-NEXT: call void @no_side_effects0()
; CHECK-NEXT: br label [[IF_END:%.*]]
; CHECK: if.else:
; CHECK-NEXT: call void @no_side_effects1()
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: store i16 [[U]], ptr [[D:%.*]], align 2
; CHECK-NEXT: ret void
;
entry:
br i1 %c, label %if.then, label %if.else

if.then:
%0 = load i16, ptr %b, align 2
call void @no_side_effects0()
%div.0 = sdiv i16 211, %0
%u = add i16 %div.0, %0
br label %if.end

if.else:
%1 = load i16, ptr %b, align 2
call void @no_side_effects1()
%div.1 = sdiv i16 211, %1
%v = add i16 %div.1, %1
br label %if.end

if.end:
%uv = phi i16 [ %v, %if.else ], [ %u, %if.then ]
store i16 %uv, ptr %d, align 2
ret void
}

declare void @side_effects0()
declare void @side_effects1()
declare void @no_side_effects0() readonly nounwind willreturn
declare void @no_side_effects1() readonly nounwind willreturn