Skip to content

Commit cc121bd

Browse files
committed
[CIR][Test] Add comprehensive divergence and crash test suite
This commit adds 77 divergence tests and 6 crash tests documenting current gaps between ClangIR's direct-to-LLVM lowering and standard CodeGen. These tests serve as a systematic tracking mechanism for correctness issues that need to be addressed for ClangIR correctness. The test suite is organized into 12 categories: **Calling Conventions (26 tests):** - Struct return ABI mismatches (sret usage, struct coercion) - Float/SSE calling convention issues - Missing LLVM attributes (noundef, nonnull, dereferenceable) **Member Pointers (12 tests):** - ABI incompatibilities in member pointer handling - Issues with virtual functions, multiple inheritance, and data members **Templates (8 tests):** - Missing comdat groups on template instantiations - Specialization and variadic template issues **Construction & Destruction (8 tests):** - Constructor variants (delegating, copy, move, parameterized) - Missing comdat on inline constructors/destructors **Inheritance (6 tests):** - Diamond inheritance, empty base optimization - Protected/private inheritance **Static Initialization (5 tests):** - Thread-local storage wrapper generation - Global and static local initialization **Lambdas (7 tests):** - Capture mechanisms and missing comdat groups **RTTI & Vtables (6 tests):** - Type info linkage issues - Missing comdat, unnamed_addr attributes - String constant null terminator issues **Array Operations (1 test):** - Array new/delete attribute and null check issues **Crash Tests (6 tests):** - Exception handling (NYI) - Multi-inheritance thunks - Virtual inheritance - Static local guards - Array new with default arguments - Computed goto All divergence tests are marked XFAIL and include: - Detailed comments explaining the divergence - RUN lines comparing CIR vs CodeGen output - CHECK directives highlighting specific patterns ghstack-source-id: c52311c Pull-Request: #2023
1 parent e3efddc commit cc121bd

File tree

83 files changed

+3151
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+3151
-0
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll
2+
// RUN: FileCheck --input-file=%t.ll %s --check-prefix=LLVM
3+
//
4+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.og.ll
5+
// RUN: FileCheck --input-file=%t.og.ll %s --check-prefix=OGCG
6+
//
7+
// XFAIL: *
8+
//
9+
// CIR crashes when using array new with default constructor arguments.
10+
//
11+
// Array new requires calling the constructor for each element. When the
12+
// constructor has default arguments, CIR's lowering fails with:
13+
// 'cir.const' op operation destroyed but still has uses
14+
// fatal error: error in backend: operation destroyed but still has uses
15+
//
16+
// The issue is in how CIR handles default argument values when generating
17+
// the loop to initialize array elements.
18+
//
19+
// This affects any array new expression where the class has a constructor
20+
// with default parameters.
21+
22+
struct S {
23+
int x;
24+
S(int v = 0) : x(v) {} // Default argument triggers the bug
25+
~S() {}
26+
};
27+
28+
S* test_array_new() {
29+
return new S[10]; // Crashes during lowering
30+
}
31+
32+
// LLVM: Should generate array new
33+
// LLVM: define {{.*}} @_Z14test_array_newv()
34+
35+
// OGCG: Should generate array new with cookie and element loop
36+
// OGCG: define {{.*}} @_Z14test_array_newv()
37+
// OGCG: call {{.*}} @_Znam // operator new[]
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll
2+
// RUN: FileCheck --input-file=%t.ll %s --check-prefix=LLVM
3+
//
4+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.og.ll
5+
// RUN: FileCheck --input-file=%t.og.ll %s --check-prefix=OGCG
6+
//
7+
// XFAIL: *
8+
//
9+
// CIR crashes when using computed goto (GNU extension).
10+
//
11+
// Computed goto allows taking the address of a label and jumping to it
12+
// dynamically. This is implemented via the AddrLabelExpr AST node.
13+
//
14+
// Currently, CIR crashes with:
15+
// Assertion `0 && "NYI"' failed
16+
// at CIRGenExprConst.cpp:1634 in ConstantLValueEmitter::VisitAddrLabelExpr
17+
//
18+
// The issue is that CIR's constant expression emitter doesn't handle
19+
// AddrLabelExpr (&&label syntax).
20+
//
21+
// This affects code using computed goto, which is common in interpreters,
22+
// state machines, and performance-critical dispatch code.
23+
24+
int test_computed_goto(int x) {
25+
void* labels[] = {&&label0, &&label1, &&label2};
26+
27+
if (x >= 0 && x <= 2)
28+
goto *labels[x];
29+
return -1;
30+
31+
label0:
32+
return 0;
33+
label1:
34+
return 10;
35+
label2:
36+
return 20;
37+
}
38+
39+
// LLVM: Should generate indirectbr
40+
// LLVM: define {{.*}} @_Z18test_computed_gotoi({{.*}})
41+
42+
// OGCG: Should use blockaddress and indirectbr
43+
// OGCG: define {{.*}} @_Z18test_computed_gotoi({{.*}})
44+
// OGCG: blockaddress(@_Z18test_computed_gotoi
45+
// OGCG: indirectbr
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm -fcxx-exceptions -fexceptions %s -o %t.ll
2+
// RUN: FileCheck --input-file=%t.ll %s --check-prefix=LLVM
3+
//
4+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fcxx-exceptions -fexceptions %s -o %t.og.ll
5+
// RUN: FileCheck --input-file=%t.og.ll %s --check-prefix=OGCG
6+
//
7+
// XFAIL: *
8+
//
9+
// CIR crashes when handling C++ exceptions (try/catch blocks).
10+
//
11+
// Exception handling requires:
12+
// - Generating personality functions
13+
// - Landing pads for catch blocks
14+
// - Invoke instructions instead of call for functions that may throw
15+
// - Exception object allocation and cleanup
16+
//
17+
// Currently, CIR crashes with:
18+
// NYI
19+
// UNREACHABLE executed at CIRGenItaniumCXXABI.cpp:814
20+
// at emitBeginCatch
21+
//
22+
// This affects any code using try/catch/throw.
23+
24+
struct Exception {
25+
int code;
26+
Exception(int c) : code(c) {}
27+
~Exception() {}
28+
};
29+
30+
void may_throw() {
31+
throw Exception(42);
32+
}
33+
34+
int catch_exception() {
35+
try {
36+
may_throw();
37+
return 0;
38+
} catch (const Exception& e) {
39+
return e.code;
40+
}
41+
}
42+
43+
// LLVM: Should generate exception handling code
44+
// LLVM: define {{.*}} @_Z15catch_exceptionv()
45+
46+
// OGCG: Should use invoke and landing pads
47+
// OGCG: define {{.*}} @_Z15catch_exceptionv() {{.*}} personality
48+
// OGCG: invoke {{.*}} @_Z9may_throwv()
49+
// OGCG: landingpad
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll
2+
// RUN: FileCheck --input-file=%t.ll %s --check-prefix=LLVM
3+
//
4+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.og.ll
5+
// RUN: FileCheck --input-file=%t.og.ll %s --check-prefix=OGCG
6+
//
7+
// XFAIL: *
8+
//
9+
// CIR crashes when generating thunks for multiple inheritance.
10+
//
11+
// Multiple inheritance requires generating thunks to adjust the 'this' pointer
12+
// when calling virtual functions through a base class pointer that is not the
13+
// primary base. The thunk adjusts 'this' and then calls the actual implementation.
14+
//
15+
// Currently, CIR crashes with:
16+
// Assertion `isValid()' failed in Address::getPointer()
17+
// at clang::CIRGen::CIRGenFunction::emitReturnOfRValue
18+
//
19+
// This affects any class using multiple inheritance with virtual functions.
20+
21+
struct A {
22+
virtual ~A() {}
23+
virtual int foo() { return 1; }
24+
int a;
25+
};
26+
27+
struct B {
28+
virtual ~B() {}
29+
virtual int bar() { return 2; }
30+
int b;
31+
};
32+
33+
struct C : A, B {
34+
int foo() override { return 3; }
35+
int bar() override { return 4; }
36+
};
37+
38+
C* make_c() {
39+
return new C();
40+
}
41+
42+
// LLVM: Should generate thunks for B's vtable in C
43+
// LLVM: define {{.*}} @_Z6make_cv()
44+
45+
// OGCG: Should generate thunks for B's vtable in C
46+
// OGCG: define {{.*}} @_Z6make_cv()
47+
// OGCG: define {{.*}} @_ZThn{{[0-9]+}}_N1C3barEv
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll
2+
// RUN: FileCheck --input-file=%t.ll %s --check-prefix=LLVM
3+
//
4+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.og.ll
5+
// RUN: FileCheck --input-file=%t.og.ll %s --check-prefix=OGCG
6+
//
7+
// XFAIL: *
8+
//
9+
// CIR crashes when handling static local variables with constructors.
10+
//
11+
// Static locals with non-trivial constructors require thread-safe initialization
12+
// using guard variables and the __cxa_guard_acquire/__cxa_guard_release ABI.
13+
//
14+
// Per the Itanium C++ ABI:
15+
// - A guard variable tracks initialization state
16+
// - __cxa_guard_acquire checks if already initialized (returns 0 if so)
17+
// - Constructor runs once
18+
// - __cxa_guard_release marks as initialized
19+
//
20+
// Currently, CIR crashes with:
21+
// NYI: thread-safe guards with __cxa_guard_acquire/release
22+
// UNREACHABLE executed at LoweringPrepare.cpp:938
23+
// at lowerGuardedInitOp
24+
//
25+
// This affects any function with static local variables that have constructors.
26+
27+
struct GlobalClass {
28+
int value;
29+
GlobalClass(int v) : value(v) {}
30+
~GlobalClass() {}
31+
};
32+
33+
// Static local with constructor
34+
int get_static_local() {
35+
static GlobalClass local(123);
36+
return local.value;
37+
}
38+
39+
// LLVM: Should have function definition
40+
// LLVM: define {{.*}} @_Z16get_static_localv()
41+
42+
// OGCG: Should use guard variables and cxa_guard functions
43+
// OGCG: define {{.*}} @_Z16get_static_localv()
44+
// OGCG: call {{.*}} @__cxa_guard_acquire
45+
// OGCG: call {{.*}} @__cxa_guard_release
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll
2+
// RUN: FileCheck --input-file=%t.ll %s --check-prefix=LLVM
3+
//
4+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.og.ll
5+
// RUN: FileCheck --input-file=%t.og.ll %s --check-prefix=OGCG
6+
//
7+
// XFAIL: *
8+
//
9+
// CIR crashes when handling virtual inheritance with thunks.
10+
//
11+
// Virtual inheritance requires:
12+
// - VTT (Virtual Table Table) for construction
13+
// - Virtual base pointer adjustments in thunks
14+
// - vtable offset lookups for dynamic adjustment
15+
//
16+
// Currently, CIR crashes with:
17+
// Virtual adjustment NYI - requires vtable offset lookup
18+
// UNREACHABLE executed at CIRGenItaniumCXXABI.cpp:2203
19+
// at performTypeAdjustment during thunk generation
20+
//
21+
// This affects any class hierarchy using virtual inheritance.
22+
23+
struct Base {
24+
virtual ~Base() {}
25+
int b;
26+
};
27+
28+
struct A : virtual Base {
29+
int a;
30+
};
31+
32+
struct B : virtual Base {
33+
int b;
34+
};
35+
36+
struct C : A, B {
37+
int c;
38+
};
39+
40+
C* make_c() {
41+
return new C();
42+
}
43+
44+
// LLVM: Should generate class with virtual inheritance
45+
// LLVM: define {{.*}} @_Z6make_cv()
46+
47+
// OGCG: Should generate VTT and virtual base thunks
48+
// OGCG: define {{.*}} @_Z6make_cv()
49+
// OGCG: @_ZTT1C = {{.*}} VTT for C
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.cir.ll
2+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
3+
// RUN: diff -u %t.ll %t.cir.ll | FileCheck %s --check-prefix=DIFF
4+
//
5+
// XFAIL: *
6+
//
7+
// Array new/delete divergences:
8+
// 1. Missing nobuiltin attribute on operator new[] and operator delete[]
9+
// 2. Missing allocsize(0) attribute on operator new[]
10+
// 3. Missing null check before delete[]
11+
// 4. Missing inbounds on getelementptr
12+
// 5. Missing noundef/nonnull on function declarations
13+
//
14+
// CodeGen:
15+
// declare noundef nonnull ptr @_Znam(i64 noundef) #1
16+
// ; Function Attrs: nobuiltin allocsize(0)
17+
//
18+
// declare void @_ZdaPv(ptr noundef) #2
19+
// ; Function Attrs: nobuiltin nounwind
20+
//
21+
// %isnull = icmp eq ptr %arr, null
22+
// br i1 %isnull, label %delete.end, label %delete.notnull
23+
// delete.notnull:
24+
// call void @_ZdaPv(ptr noundef %arr)
25+
//
26+
// %arrayidx = getelementptr inbounds i32, ptr %arr, i64 0
27+
//
28+
// CIR:
29+
// declare ptr @_Znam(i64) (missing noundef, nonnull, nobuiltin, allocsize)
30+
// declare void @_ZdaPv(ptr) (missing noundef, nobuiltin, nounwind)
31+
//
32+
// call void @_ZdaPv(ptr %arr) (no null check)
33+
//
34+
// %arrayidx = getelementptr i32, ptr %arr, i64 0 (missing inbounds)
35+
36+
// DIFF: -declare noundef nonnull ptr @_Znam(i64 noundef)
37+
// DIFF: +declare ptr @_Znam(i64)
38+
// DIFF: -; Function Attrs: nobuiltin allocsize(0)
39+
// DIFF: -declare void @_ZdaPv(ptr noundef)
40+
// DIFF: +declare void @_ZdaPv(ptr)
41+
// DIFF: -; Function Attrs: nobuiltin nounwind
42+
// DIFF: -%isnull = icmp eq ptr
43+
// DIFF: -br i1 %isnull
44+
// DIFF: -delete.notnull:
45+
// DIFF: getelementptr inbounds
46+
// DIFF: -getelementptr inbounds
47+
// DIFF: +getelementptr
48+
49+
int test() {
50+
int* arr = new int[10];
51+
arr[0] = 42;
52+
int result = arr[0];
53+
delete[] arr;
54+
return result;
55+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.cir.ll
2+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
3+
// RUN: diff -u %t.ll %t.cir.ll | FileCheck %s --check-prefix=DIFF
4+
//
5+
// XFAIL: *
6+
//
7+
// 12-byte structs (three ints) should be coerced to { i64, i32 } per x86_64 ABI.
8+
//
9+
// CodeGen coerces to two registers:
10+
// define { i64, i32 } @return_three_ints()
11+
//
12+
// CIR returns the struct directly:
13+
// define %struct.ThreeInts @return_three_ints()
14+
15+
// DIFF: -define {{.*}} { i64, i32 } @{{.*}}return_three_ints
16+
// DIFF: +define {{.*}} %struct.ThreeInts @{{.*}}return_three_ints
17+
18+
struct ThreeInts {
19+
int a, b, c; // 12 bytes total
20+
};
21+
22+
ThreeInts return_three_ints() {
23+
return {1, 2, 3};
24+
}
25+
26+
int take_three_ints(ThreeInts s) {
27+
return s.a + s.b + s.c;
28+
}
29+
30+
int test() {
31+
ThreeInts s = return_three_ints();
32+
return take_three_ints(s);
33+
}

0 commit comments

Comments
 (0)