Skip to content

Commit 44e270a

Browse files
committed
[CIR] Add support for five trivial expression types
This patch implements support for five straightforward C++ expression types that can be directly ported from traditional CodeGen: 1. SizeOfPackExpr - C++11 variadic template pack size operator 2. FixedPointLiteral - Embedded fixed-point arithmetic literals 3. MaterializeTemporaryExpr - Temporary lifetime extension for const refs 4. ConceptSpecializationExpr - C++20 concept satisfaction checks 5. RequiresExpr - C++20 requires expression evaluation All implementations follow the same pattern as CGExprScalar.cpp, generating compile-time constant values where appropriate. Each feature includes comprehensive test coverage. These are simple additions that increase C++ language coverage without introducing new high-level CIR abstractions. ghstack-source-id: 1a523d0 Pull-Request: #1987
1 parent d1b9c55 commit 44e270a

File tree

6 files changed

+268
-5
lines changed

6 files changed

+268
-5
lines changed

clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,8 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
184184
}
185185

186186
mlir::Value VisitFixedPointLiteral(const FixedPointLiteral *E) {
187-
llvm_unreachable("NYI");
187+
return Builder.getConstAPInt(CGF.getLoc(E->getExprLoc()),
188+
convertType(E->getType()), E->getValue());
188189
}
189190
mlir::Value VisitFloatingLiteral(const FloatingLiteral *E) {
190191
mlir::Type Ty = CGF.convertType(E->getType());
@@ -248,7 +249,8 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
248249
}
249250

250251
mlir::Value VisitSizeOfPackExpr(SizeOfPackExpr *E) {
251-
llvm_unreachable("NYI");
252+
return Builder.getConstInt(CGF.getLoc(E->getExprLoc()),
253+
convertType(E->getType()), E->getPackLength());
252254
}
253255
mlir::Value VisitPseudoObjectExpr(PseudoObjectExpr *E) {
254256
return CGF.emitPseudoObjectRValue(E).getScalarVal();
@@ -701,7 +703,7 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
701703

702704
// C++
703705
mlir::Value VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E) {
704-
llvm_unreachable("NYI");
706+
return emitLoadOfLValue(E);
705707
}
706708
mlir::Value VisitSourceLocExpr(SourceLocExpr *E) { llvm_unreachable("NYI"); }
707709
mlir::Value VisitCXXDefaultArgExpr(CXXDefaultArgExpr *DAE) {
@@ -734,10 +736,10 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
734736
}
735737
mlir::Value
736738
VisitConceptSpecializationExpr(const ConceptSpecializationExpr *E) {
737-
llvm_unreachable("NYI");
739+
return Builder.getBool(E->isSatisfied(), CGF.getLoc(E->getExprLoc()));
738740
}
739741
mlir::Value VisitRequiresExpr(const RequiresExpr *E) {
740-
llvm_unreachable("NYI");
742+
return Builder.getBool(E->isSatisfied(), CGF.getLoc(E->getExprLoc()));
741743
}
742744
mlir::Value VisitArrayTypeTraitExpr(const ArrayTypeTraitExpr *E) {
743745
llvm_unreachable("NYI");
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s
3+
4+
template <typename T>
5+
concept Integral = __is_integral(T);
6+
7+
template <typename T>
8+
concept Signed = Integral<T> && __is_signed(T);
9+
10+
// Test ConceptSpecializationExpr as a boolean value
11+
bool test_concept_bool() {
12+
// CHECK-LABEL: cir.func {{.*}}@{{.*}}test_concept_boolv
13+
// CHECK: %{{.*}} = cir.const #true
14+
return Integral<int>;
15+
}
16+
17+
bool test_concept_false() {
18+
// CHECK-LABEL: cir.func {{.*}}@{{.*}}test_concept_falsev
19+
// CHECK: %{{.*}} = cir.const #false
20+
return Integral<float>;
21+
}
22+
23+
bool test_concept_compound() {
24+
// CHECK-LABEL: cir.func {{.*}}@{{.*}}test_concept_compoundv
25+
// CHECK: %{{.*}} = cir.const #true
26+
return Signed<int>;
27+
}
28+
29+
bool test_concept_unsigned() {
30+
// CHECK-LABEL: cir.func {{.*}}@{{.*}}test_concept_unsignedv
31+
// CHECK: %{{.*}} = cir.const #false
32+
return Signed<unsigned>;
33+
}
34+
35+
// Test in conditional
36+
int test_concept_in_if() {
37+
// CHECK-LABEL: cir.func {{.*}}@{{.*}}test_concept_in_ifv
38+
if (Integral<int>) {
39+
// CHECK: %{{.*}} = cir.const #true
40+
// CHECK: cir.if %{{.*}} {
41+
return 1;
42+
}
43+
return 0;
44+
}
45+
46+
// Test constexpr variable with concept
47+
constexpr bool is_int_integral = Integral<int>;
48+
49+
int use_constexpr() {
50+
// CHECK-LABEL: cir.func {{.*}}@{{.*}}use_constexprv
51+
if (is_int_integral) {
52+
// This should be optimized to a constant true
53+
return 42;
54+
}
55+
return 0;
56+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -ffixed-point -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s
3+
4+
// Test basic fixed-point literals
5+
void test_short_fract() {
6+
// CHECK: cir.func {{.*}}@test_short_fract
7+
short _Fract sf = 0.5hr;
8+
// CHECK: %{{.*}} = cir.const #cir.int<64> : !s8i
9+
10+
unsigned short _Fract usf = 0.5uhr;
11+
// CHECK: %{{.*}} = cir.const #cir.int<128> : !u8i
12+
}
13+
14+
void test_fract() {
15+
// CHECK: cir.func {{.*}}@test_fract
16+
_Fract f = 0.5r;
17+
// CHECK: %{{.*}} = cir.const #cir.int<16384> : !s16i
18+
19+
unsigned _Fract uf = 0.5ur;
20+
// CHECK: %{{.*}} = cir.const #cir.int<32768> : !u16i
21+
}
22+
23+
void test_long_fract() {
24+
// CHECK: cir.func {{.*}}@test_long_fract
25+
long _Fract lf = 0.5lr;
26+
// CHECK: %{{.*}} = cir.const #cir.int<1073741824> : !s32i
27+
}
28+
29+
void test_accum() {
30+
// CHECK: cir.func {{.*}}@test_accum
31+
short _Accum sa = 0.5hk;
32+
// CHECK: %{{.*}} = cir.const #cir.int<64> : !s16i
33+
}
34+
35+
void test_negative() {
36+
// CHECK: cir.func {{.*}}@test_negative
37+
short _Fract sf = -0.5hr;
38+
// CHECK: %{{.*}} = cir.const #cir.int<64> : !s8i
39+
// CHECK: %{{.*}} = cir.unary(minus, %{{.*}}) : !s8i, !s8i
40+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s
3+
4+
// Test MaterializeTemporaryExpr when binding const reference to rvalue
5+
int get_value() { return 42; }
6+
7+
void test_const_ref_binding() {
8+
// CHECK-LABEL: cir.func {{.*}}@{{.*}}test_const_ref_bindingv
9+
const int &x = 5;
10+
// CHECK: %{{.*}} = cir.alloca !s32i, !cir.ptr<!s32i>, ["ref.tmp0", init]
11+
// CHECK: %{{.*}} = cir.alloca !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>, ["x", init, const]
12+
// CHECK: cir.scope {
13+
// CHECK: %{{.*}} = cir.const #cir.int<5> : !s32i
14+
// CHECK: cir.store {{.*}} %{{.*}}, %{{.*}} : !s32i, !cir.ptr<!s32i>
15+
// CHECK: }
16+
}
17+
18+
void test_const_ref_expr() {
19+
// CHECK-LABEL: cir.func {{.*}}@{{.*}}test_const_ref_exprv
20+
const int &y = get_value();
21+
// CHECK: %{{.*}} = cir.alloca !s32i, !cir.ptr<!s32i>, ["ref.tmp0", init]
22+
// CHECK: %{{.*}} = cir.alloca !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>, ["y", init, const]
23+
// CHECK: cir.scope {
24+
// CHECK: %{{.*}} = cir.call @{{.*}}get_valuev()
25+
// CHECK: }
26+
}
27+
28+
void test_const_ref_arithmetic() {
29+
// CHECK-LABEL: cir.func {{.*}}@{{.*}}test_const_ref_arithmeticv
30+
int a = 10;
31+
const int &z = a + 5;
32+
// CHECK: %{{.*}} = cir.alloca !s32i, !cir.ptr<!s32i>, ["ref.tmp0", init]
33+
// CHECK: %{{.*}} = cir.alloca !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>, ["z", init, const]
34+
// CHECK: cir.scope {
35+
// CHECK: %{{.*}} = cir.load {{.*}} %{{.*}}
36+
// CHECK: %{{.*}} = cir.const #cir.int<5> : !s32i
37+
// CHECK: %{{.*}} = cir.binop(add, %{{.*}}, %{{.*}})
38+
// CHECK: }
39+
}
40+
41+
struct S {
42+
int val;
43+
S(int v) : val(v) {}
44+
};
45+
46+
S make_s() { return S(100); }
47+
48+
void test_const_ref_struct() {
49+
// CHECK-LABEL: cir.func {{.*}}@{{.*}}test_const_ref_structv
50+
const S &s = make_s();
51+
// Temporary S object should be materialized
52+
// CHECK: %{{.*}} = cir.alloca {{.*}}, !cir.ptr<{{.*}}rec_S{{.*}}>, ["ref.tmp0"]
53+
// CHECK: %{{.*}} = cir.alloca !cir.ptr<{{.*}}>, !cir.ptr<!cir.ptr<{{.*}}>>, ["s", init, const]
54+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s
3+
4+
// Test RequiresExpr as a boolean expression
5+
bool test_requires_simple() {
6+
// CHECK-LABEL: cir.func {{.*}}@{{.*}}test_requires_simplev
7+
// CHECK: %{{.*}} = cir.const #true
8+
return requires { 1 + 1; };
9+
}
10+
11+
template <typename T>
12+
bool test_requires_param() {
13+
return requires(T t) { t + 1; };
14+
}
15+
16+
bool use_requires_param() {
17+
// CHECK-LABEL: cir.func {{.*}}@{{.*}}use_requires_paramv
18+
// Instantiation with int should succeed
19+
return test_requires_param<int>();
20+
// CHECK: cir.call @{{.*}}test_requires_paramIiEbv
21+
}
22+
23+
// Test requires expression with multiple requirements
24+
bool test_requires_multiple() {
25+
// CHECK-LABEL: cir.func {{.*}}@{{.*}}test_requires_multiplev
26+
// CHECK: %{{.*}} = cir.const #true
27+
return requires {
28+
1 + 1;
29+
2 * 2;
30+
};
31+
}
32+
33+
// Test requires expression in if statement
34+
int test_requires_in_if() {
35+
// CHECK-LABEL: cir.func {{.*}}@{{.*}}test_requires_in_ifv
36+
if (requires { 1 + 1; }) {
37+
// CHECK: %{{.*}} = cir.const #true
38+
// CHECK: cir.if %{{.*}} {
39+
return 1;
40+
}
41+
return 0;
42+
}
43+
44+
// Test requires expression that should fail
45+
template <typename T>
46+
bool test_requires_fail() {
47+
return requires { T::nonexistent_member; };
48+
}
49+
50+
bool use_requires_fail() {
51+
// CHECK-LABEL: cir.func {{.*}}@{{.*}}use_requires_failv
52+
// Should return false for int (no member named nonexistent_member)
53+
return test_requires_fail<int>();
54+
// CHECK: cir.call @{{.*}}test_requires_failIiEbv
55+
}
56+
57+
// Test nested requires
58+
bool test_nested_requires() {
59+
// CHECK-LABEL: cir.func {{.*}}@{{.*}}test_nested_requiresv
60+
// CHECK: %{{.*}} = cir.const #true
61+
return requires {
62+
requires true;
63+
};
64+
}
65+
66+
// Use in constexpr context
67+
constexpr bool can_add_int = requires(int a, int b) { a + b; };
68+
69+
int use_constexpr_requires() {
70+
// CHECK-LABEL: cir.func {{.*}}@{{.*}}use_constexpr_requiresv
71+
if (can_add_int) {
72+
return 42;
73+
}
74+
return 0;
75+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s
3+
4+
// Test basic sizeof... on type parameter pack
5+
template<typename ...Types>
6+
int get_num_types(Types...) {
7+
return sizeof...(Types);
8+
}
9+
10+
// CHECK-LABEL: cir.func {{.*}}@{{.*}}get_num_typesIJifdEEiDpT_
11+
// CHECK: %{{.*}} = cir.const #cir.int<3> : !u64i
12+
// CHECK: %{{.*}} = cir.cast integral %{{.*}} : !u64i -> !s32i
13+
14+
template int get_num_types(int, float, double);
15+
16+
// Test sizeof... with empty pack
17+
template<typename ...Types>
18+
int get_num_empty(Types...) {
19+
return sizeof...(Types);
20+
}
21+
22+
// CHECK-LABEL: cir.func {{.*}}@{{.*}}get_num_emptyIJEEiDpT_
23+
// CHECK: %{{.*}} = cir.const #cir.int<0> : !u64i
24+
25+
template int get_num_empty();
26+
27+
// Test sizeof... on non-type parameter pack
28+
template<int... Vals>
29+
int count_values() {
30+
return sizeof...(Vals);
31+
}
32+
33+
// CHECK-LABEL: cir.func {{.*}}@{{.*}}count_valuesIJLi1ELi2ELi3ELi4ELi5EEEiv
34+
// CHECK: %{{.*}} = cir.const #cir.int<5> : !u64i
35+
36+
template int count_values<1, 2, 3, 4, 5>();

0 commit comments

Comments
 (0)