This implements support for [named loops](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3355.htm) for C2y. When parsing a `LabelStmt`, we create the `LabeDecl` early before we parse the substatement; this label is then passed down to `ParseWhileStatement()` and friends, which then store it in the loop’s (or switch statement’s) `Scope`; when we encounter a `break/continue` statement, we perform a lookup for the label (and error if it doesn’t exist), and then walk the scope stack and check if there is a scope whose preceding label is the target label, which identifies the jump target. The feature is only supported in C2y mode, though a cc1-only option exists for testing (`-fnamed-loops`), which is mostly intended to try and make sure that we don’t have to refactor this entire implementation when/if we start supporting it in C++. --------- Co-authored-by: Balazs Benics <benicsbalazs@gmail.com>
222 lines
7.5 KiB
C++
222 lines
7.5 KiB
C++
// RUN: %clang_cc1 -fnamed-loops -triple x86_64-unknown-linux -std=c++20 -emit-llvm -o - %s | FileCheck %s
|
|
|
|
static int a[10]{};
|
|
struct NonTrivialDestructor {
|
|
~NonTrivialDestructor();
|
|
};
|
|
|
|
bool g(int);
|
|
bool h();
|
|
|
|
// CHECK-LABEL: define {{.*}} void @_Z2f1v()
|
|
// CHECK: entry:
|
|
// CHECK: %__range1 = alloca ptr, align 8
|
|
// CHECK: %__begin1 = alloca ptr, align 8
|
|
// CHECK: %__end1 = alloca ptr, align 8
|
|
// CHECK: %i = alloca i32, align 4
|
|
// CHECK: br label %x
|
|
// CHECK: x:
|
|
// CHECK: store ptr @_ZL1a, ptr %__range1, align 8
|
|
// CHECK: store ptr @_ZL1a, ptr %__begin1, align 8
|
|
// CHECK: store ptr getelementptr inbounds (i32, ptr @_ZL1a, i64 10), ptr %__end1, align 8
|
|
// CHECK: br label %for.cond
|
|
// CHECK: for.cond:
|
|
// CHECK: %0 = load ptr, ptr %__begin1, align 8
|
|
// CHECK: %1 = load ptr, ptr %__end1, align 8
|
|
// CHECK: %cmp = icmp ne ptr %0, %1
|
|
// CHECK: br i1 %cmp, label %for.body, label %for.end
|
|
// CHECK: for.body:
|
|
// CHECK: %2 = load ptr, ptr %__begin1, align 8
|
|
// CHECK: %3 = load i32, ptr %2, align 4
|
|
// CHECK: store i32 %3, ptr %i, align 4
|
|
// CHECK: %4 = load i32, ptr %i, align 4
|
|
// CHECK: %call = call {{.*}} i1 @_Z1gi(i32 {{.*}} %4)
|
|
// CHECK: br i1 %call, label %if.then, label %if.end
|
|
// CHECK: if.then:
|
|
// CHECK: br label %for.end
|
|
// CHECK: if.end:
|
|
// CHECK: %5 = load i32, ptr %i, align 4
|
|
// CHECK: %call1 = call {{.*}} i1 @_Z1gi(i32 {{.*}} %5)
|
|
// CHECK: br i1 %call1, label %if.then2, label %if.end3
|
|
// CHECK: if.then2:
|
|
// CHECK: br label %for.inc
|
|
// CHECK: if.end3:
|
|
// CHECK: br label %for.inc
|
|
// CHECK: for.inc:
|
|
// CHECK: %6 = load ptr, ptr %__begin1, align 8
|
|
// CHECK: %incdec.ptr = getelementptr inbounds nuw i32, ptr %6, i32 1
|
|
// CHECK: store ptr %incdec.ptr, ptr %__begin1, align 8
|
|
// CHECK: br label %for.cond
|
|
// CHECK: for.end:
|
|
// CHECK: ret void
|
|
void f1() {
|
|
x: for (int i : a) {
|
|
if (g(i)) break x;
|
|
if (g(i)) continue x;
|
|
}
|
|
}
|
|
|
|
// CHECK-LABEL: define {{.*}} void @_Z2f2v()
|
|
// CHECK: entry:
|
|
// CHECK: %n1 = alloca %struct.NonTrivialDestructor, align 1
|
|
// CHECK: %__range2 = alloca ptr, align 8
|
|
// CHECK: %__begin2 = alloca ptr, align 8
|
|
// CHECK: %__end2 = alloca ptr, align 8
|
|
// CHECK: %i = alloca i32, align 4
|
|
// CHECK: %n2 = alloca %struct.NonTrivialDestructor, align 1
|
|
// CHECK: %cleanup.dest.slot = alloca i32, align 4
|
|
// CHECK: %n3 = alloca %struct.NonTrivialDestructor, align 1
|
|
// CHECK: %n4 = alloca %struct.NonTrivialDestructor, align 1
|
|
// CHECK: br label %l1
|
|
// CHECK: l1:
|
|
// CHECK: br label %while.cond
|
|
// CHECK: while.cond:
|
|
// CHECK: %call = call {{.*}} i1 @_Z1gi(i32 {{.*}} 0)
|
|
// CHECK: br i1 %call, label %while.body, label %while.end
|
|
// CHECK: while.body:
|
|
// CHECK: br label %l2
|
|
// CHECK: l2:
|
|
// CHECK: store ptr @_ZL1a, ptr %__range2, align 8
|
|
// CHECK: store ptr @_ZL1a, ptr %__begin2, align 8
|
|
// CHECK: store ptr getelementptr inbounds (i32, ptr @_ZL1a, i64 10), ptr %__end2, align 8
|
|
// CHECK: br label %for.cond
|
|
// CHECK: for.cond:
|
|
// CHECK: %0 = load ptr, ptr %__begin2, align 8
|
|
// CHECK: %1 = load ptr, ptr %__end2, align 8
|
|
// CHECK: %cmp = icmp ne ptr %0, %1
|
|
// CHECK: br i1 %cmp, label %for.body, label %for.end
|
|
// CHECK: for.body:
|
|
// CHECK: %2 = load ptr, ptr %__begin2, align 8
|
|
// CHECK: %3 = load i32, ptr %2, align 4
|
|
// CHECK: store i32 %3, ptr %i, align 4
|
|
// CHECK: %4 = load i32, ptr %i, align 4
|
|
// CHECK: %call1 = call {{.*}} i1 @_Z1gi(i32 {{.*}} %4)
|
|
// CHECK: br i1 %call1, label %if.then, label %if.end
|
|
// CHECK: if.then:
|
|
// CHECK: store i32 4, ptr %cleanup.dest.slot, align 4
|
|
// CHECK: br label %cleanup
|
|
// CHECK: if.end:
|
|
// CHECK: %5 = load i32, ptr %i, align 4
|
|
// CHECK: %call2 = call {{.*}} i1 @_Z1gi(i32 {{.*}} %5)
|
|
// CHECK: br i1 %call2, label %if.then3, label %if.end4
|
|
// CHECK: if.then3:
|
|
// CHECK: store i32 3, ptr %cleanup.dest.slot, align 4
|
|
// CHECK: br label %cleanup
|
|
// CHECK: if.end4:
|
|
// CHECK: %6 = load i32, ptr %i, align 4
|
|
// CHECK: %call5 = call {{.*}} i1 @_Z1gi(i32 {{.*}} %6)
|
|
// CHECK: br i1 %call5, label %if.then6, label %if.end7
|
|
// CHECK: if.then6:
|
|
// CHECK: store i32 6, ptr %cleanup.dest.slot, align 4
|
|
// CHECK: br label %cleanup
|
|
// CHECK: if.end7:
|
|
// CHECK: %7 = load i32, ptr %i, align 4
|
|
// CHECK: %call8 = call {{.*}} i1 @_Z1gi(i32 {{.*}} %7)
|
|
// CHECK: br i1 %call8, label %if.then9, label %if.end10
|
|
// CHECK: if.then9:
|
|
// CHECK: store i32 7, ptr %cleanup.dest.slot, align 4
|
|
// CHECK: br label %cleanup
|
|
// CHECK: if.end10:
|
|
// CHECK: call void @_ZN20NonTrivialDestructorD1Ev(ptr {{.*}} %n3)
|
|
// CHECK: store i32 0, ptr %cleanup.dest.slot, align 4
|
|
// CHECK: br label %cleanup
|
|
// CHECK: cleanup:
|
|
// CHECK: call void @_ZN20NonTrivialDestructorD1Ev(ptr {{.*}} %n2)
|
|
// CHECK: %cleanup.dest = load i32, ptr %cleanup.dest.slot, align 4
|
|
// CHECK: switch i32 %cleanup.dest, label %cleanup11 [
|
|
// CHECK: i32 0, label %cleanup.cont
|
|
// CHECK: i32 6, label %for.end
|
|
// CHECK: i32 7, label %for.inc
|
|
// CHECK: ]
|
|
// CHECK: cleanup.cont:
|
|
// CHECK: br label %for.inc
|
|
// CHECK: for.inc:
|
|
// CHECK: %8 = load ptr, ptr %__begin2, align 8
|
|
// CHECK: %incdec.ptr = getelementptr inbounds nuw i32, ptr %8, i32 1
|
|
// CHECK: store ptr %incdec.ptr, ptr %__begin2, align 8
|
|
// CHECK: br label %for.cond
|
|
// CHECK: for.end:
|
|
// CHECK: call void @_ZN20NonTrivialDestructorD1Ev(ptr {{.*}} %n4)
|
|
// CHECK: store i32 0, ptr %cleanup.dest.slot, align 4
|
|
// CHECK: br label %cleanup11
|
|
// CHECK: cleanup11:
|
|
// CHECK: call void @_ZN20NonTrivialDestructorD1Ev(ptr {{.*}} %n1)
|
|
// CHECK: %cleanup.dest12 = load i32, ptr %cleanup.dest.slot, align 4
|
|
// CHECK: switch i32 %cleanup.dest12, label %unreachable [
|
|
// CHECK: i32 0, label %cleanup.cont13
|
|
// CHECK: i32 4, label %while.end
|
|
// CHECK: i32 3, label %while.cond
|
|
// CHECK: ]
|
|
// CHECK: cleanup.cont13:
|
|
// CHECK: br label %while.cond
|
|
// CHECK: while.end:
|
|
// CHECK: ret void
|
|
// CHECK: unreachable:
|
|
// CHECK: unreachable
|
|
void f2() {
|
|
l1: while (g(0)) {
|
|
NonTrivialDestructor n1;
|
|
l2: for (int i : a) {
|
|
NonTrivialDestructor n2;
|
|
if (g(i)) break l1;
|
|
if (g(i)) continue l1;
|
|
if (g(i)) break l2;
|
|
if (g(i)) continue l2;
|
|
NonTrivialDestructor n3;
|
|
}
|
|
NonTrivialDestructor n4;
|
|
}
|
|
}
|
|
|
|
template <bool Continue>
|
|
void f3() {
|
|
l1: while (g(1)) {
|
|
for (;g(2);) {
|
|
if constexpr (Continue) continue l1;
|
|
else break l1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// CHECK-LABEL: define {{.*}} void @_Z2f3ILb1EEvv()
|
|
// CHECK: entry:
|
|
// CHECK: br label %l1
|
|
// CHECK: l1:
|
|
// CHECK: br label %while.cond
|
|
// CHECK: while.cond:
|
|
// CHECK: %call = call {{.*}} i1 @_Z1gi(i32 {{.*}} 1)
|
|
// CHECK: br i1 %call, label %while.body, label %while.end
|
|
// CHECK: while.body:
|
|
// CHECK: br label %for.cond
|
|
// CHECK: for.cond:
|
|
// CHECK: %call1 = call {{.*}} i1 @_Z1gi(i32 {{.*}} 2)
|
|
// CHECK: br i1 %call1, label %for.body, label %for.end
|
|
// CHECK: for.body:
|
|
// CHECK: br label %while.cond
|
|
// CHECK: for.end:
|
|
// CHECK: br label %while.cond
|
|
// CHECK: while.end:
|
|
// CHECK: ret void
|
|
template void f3<true>();
|
|
|
|
// CHECK-LABEL: define {{.*}} void @_Z2f3ILb0EEvv()
|
|
// CHECK: entry:
|
|
// CHECK: br label %l1
|
|
// CHECK: l1:
|
|
// CHECK: br label %while.cond
|
|
// CHECK: while.cond:
|
|
// CHECK: %call = call {{.*}} i1 @_Z1gi(i32 {{.*}} 1)
|
|
// CHECK: br i1 %call, label %while.body, label %while.end
|
|
// CHECK: while.body:
|
|
// CHECK: br label %for.cond
|
|
// CHECK: for.cond:
|
|
// CHECK: %call1 = call {{.*}} i1 @_Z1gi(i32 {{.*}} 2)
|
|
// CHECK: br i1 %call1, label %for.body, label %for.end
|
|
// CHECK: for.body:
|
|
// CHECK: br label %while.end
|
|
// CHECK: for.end:
|
|
// CHECK: br label %while.cond
|
|
// CHECK: while.end:
|
|
// CHECK: ret void
|
|
template void f3<false>();
|