Skip to content

Commit 7bc40a6

Browse files
andykaylorkcloudy0717
authored andcommitted
[CIR] Upstream support for builtin_constant_p (llvm#170354)
This upstreams the handler for the BI__builtin_constant_p function.
1 parent 3839b65 commit 7bc40a6

File tree

4 files changed

+356
-0
lines changed

4 files changed

+356
-0
lines changed

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,6 +1173,35 @@ def CIR_SwitchOp : CIR_Op<"switch", [
11731173
let hasLLVMLowering = false;
11741174
}
11751175

1176+
//===----------------------------------------------------------------------===//
1177+
// IsConstantOp
1178+
//===----------------------------------------------------------------------===//
1179+
1180+
def CIR_IsConstantOp : CIR_Op<"is_constant", [Pure]> {
1181+
let summary = "Test for manifest compile-time constant";
1182+
let description = [{
1183+
Returns `true` if the argument is known to be a manifest compile-time
1184+
constant otherwise returns `false`. If the argument is a constant expression
1185+
which refers to a global (the address of which _is_ a constant, but not
1186+
manifest during the compile), then the intrinsic evaluates to `false`.
1187+
1188+
This is used to represent `__builtin_constant_p` in cases where the argument
1189+
isn't known to be constant during initial translation of the source code but
1190+
might be proven to be constant after later optimizations.
1191+
1192+
Example:
1193+
```
1194+
%1 = cir.is_constant %2 : !s32i -> !cir.bool
1195+
```
1196+
}];
1197+
let arguments = (ins CIR_AnyType:$val);
1198+
let results = (outs CIR_BoolType:$result);
1199+
1200+
let assemblyFormat = [{
1201+
$val `:` qualified(type($val)) `->` qualified(type($result)) attr-dict
1202+
}];
1203+
}
1204+
11761205
//===----------------------------------------------------------------------===//
11771206
// SwitchFlatOp
11781207
//===----------------------------------------------------------------------===//

clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,45 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
542542
return emitCall(e->getCallee()->getType(), CIRGenCallee::forDirect(fnOp), e,
543543
returnValue);
544544
}
545+
546+
case Builtin::BI__builtin_constant_p: {
547+
mlir::Type resultType = convertType(e->getType());
548+
549+
const Expr *arg = e->getArg(0);
550+
QualType argType = arg->getType();
551+
// FIXME: The allowance for Obj-C pointers and block pointers is historical
552+
// and likely a mistake.
553+
if (!argType->isIntegralOrEnumerationType() && !argType->isFloatingType() &&
554+
!argType->isObjCObjectPointerType() && !argType->isBlockPointerType()) {
555+
// Per the GCC documentation, only numeric constants are recognized after
556+
// inlining.
557+
return RValue::get(
558+
builder.getConstInt(getLoc(e->getSourceRange()),
559+
mlir::cast<cir::IntType>(resultType), 0));
560+
}
561+
562+
if (arg->HasSideEffects(getContext())) {
563+
// The argument is unevaluated, so be conservative if it might have
564+
// side-effects.
565+
return RValue::get(
566+
builder.getConstInt(getLoc(e->getSourceRange()),
567+
mlir::cast<cir::IntType>(resultType), 0));
568+
}
569+
570+
mlir::Value argValue = emitScalarExpr(arg);
571+
if (argType->isObjCObjectPointerType()) {
572+
cgm.errorNYI(e->getSourceRange(),
573+
"__builtin_constant_p: Obj-C object pointer");
574+
return {};
575+
}
576+
argValue = builder.createBitcast(argValue, convertType(argType));
577+
578+
mlir::Value result = cir::IsConstantOp::create(
579+
builder, getLoc(e->getSourceRange()), argValue);
580+
// IsConstantOp returns a bool, but __builtin_constant_p returns an int.
581+
result = builder.createBoolToInt(result, resultType);
582+
return RValue::get(result);
583+
}
545584
case Builtin::BI__builtin_dynamic_object_size:
546585
case Builtin::BI__builtin_object_size: {
547586
unsigned type =

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3979,6 +3979,13 @@ mlir::LogicalResult CIRToLLVMGetBitfieldOpLowering::matchAndRewrite(
39793979
return mlir::success();
39803980
}
39813981

3982+
mlir::LogicalResult CIRToLLVMIsConstantOpLowering::matchAndRewrite(
3983+
cir::IsConstantOp op, OpAdaptor adaptor,
3984+
mlir::ConversionPatternRewriter &rewriter) const {
3985+
rewriter.replaceOpWithNewOp<mlir::LLVM::IsConstantOp>(op, adaptor.getVal());
3986+
return mlir::success();
3987+
}
3988+
39823989
mlir::LogicalResult CIRToLLVMInlineAsmOpLowering::matchAndRewrite(
39833990
cir::InlineAsmOp op, OpAdaptor adaptor,
39843991
mlir::ConversionPatternRewriter &rewriter) const {
Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
3+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
4+
// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
5+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
6+
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
7+
8+
int a = 42;
9+
10+
/* --- Compound literals */
11+
12+
struct foo { int x, y; };
13+
14+
int y;
15+
struct foo f = (struct foo){ __builtin_constant_p(y), 42 };
16+
17+
// CIR: cir.global external @f = #cir.const_record<{#cir.int<0> : !s32i, #cir.int<42> : !s32i}> : !rec_foo
18+
// LLVM: @f = global %struct.foo { i32 0, i32 42 }
19+
// OGCG: @f = global %struct.foo { i32 0, i32 42 }
20+
21+
struct foo test0(int expr) {
22+
struct foo f = (struct foo){ __builtin_constant_p(expr), 42 };
23+
return f;
24+
}
25+
26+
// CIR: cir.func {{.*}} @test0(%[[ARG0:.*]]: !s32i {{.*}}) -> !rec_foo
27+
// CIR: %[[EXPR_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["expr", init]
28+
// CIR: cir.store %[[ARG0]], %[[EXPR_ADDR]]
29+
// CIR: %[[EXPR:.*]] = cir.load{{.*}} %[[EXPR_ADDR]]
30+
// CIR: %[[IS_CONSTANT:.*]] = cir.is_constant %[[EXPR]] : !s32i -> !cir.bool
31+
32+
// LLVM: define{{.*}} %struct.foo @test0(i32 %[[ARG0:.*]])
33+
// LLVM: %[[EXPR_ADDR:.*]] = alloca i32
34+
// LLVM: store i32 %[[ARG0]], ptr %[[EXPR_ADDR]]
35+
// LLVM: %[[EXPR:.*]] = load i32, ptr %[[EXPR_ADDR]]
36+
// LLVM: %[[IS_CONSTANT:.*]] = call i1 @llvm.is.constant.i32(i32 %[[EXPR]])
37+
38+
// OGCG: define{{.*}} i64 @test0(i32 {{.*}} %[[ARG0:.*]])
39+
// OGCG: %[[EXPR_ADDR:.*]] = alloca i32
40+
// OGCG: store i32 %[[ARG0]], ptr %[[EXPR_ADDR]]
41+
// OGCG: %[[EXPR:.*]] = load i32, ptr %[[EXPR_ADDR]]
42+
// OGCG: %[[IS_CONSTANT:.*]] = call i1 @llvm.is.constant.i32(i32 %[[EXPR]])
43+
44+
/* --- Pointer types */
45+
46+
int test1(void) {
47+
return __builtin_constant_p(&a - 13);
48+
}
49+
50+
// CIR: cir.func {{.*}} @test1() -> !s32i
51+
// CIR: %[[TMP1:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
52+
// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
53+
// CIR: cir.store %[[ZERO]], %[[TMP1]] : !s32i, !cir.ptr<!s32i>
54+
// CIR: %[[TMP2:.*]] = cir.load %[[TMP1]] : !cir.ptr<!s32i>, !s32i
55+
// CIR: cir.return %[[TMP2]] : !s32i
56+
57+
// LLVM: define{{.*}} i32 @test1()
58+
// LLVM: %[[TMP1:.*]] = alloca i32
59+
// LLVM: store i32 0, ptr %[[TMP1]]
60+
// LLVM: %[[TMP2:.*]] = load i32, ptr %[[TMP1]]
61+
// LLVM: ret i32 %[[TMP2]]
62+
63+
// OGCG: define{{.*}} i32 @test1()
64+
// OGCG: ret i32 0
65+
66+
/* --- Aggregate types */
67+
68+
int b[] = {1, 2, 3};
69+
70+
int test2(void) {
71+
return __builtin_constant_p(b);
72+
}
73+
74+
// CIR: cir.func {{.*}} @test2() -> !s32i
75+
// CIR: %[[TMP1:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
76+
// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
77+
// CIR: cir.store %[[ZERO]], %[[TMP1]] : !s32i, !cir.ptr<!s32i>
78+
// CIR: %[[TMP2:.*]] = cir.load %[[TMP1]] : !cir.ptr<!s32i>, !s32i
79+
// CIR: cir.return %[[TMP2]] : !s32i
80+
81+
// LLVM: define{{.*}} i32 @test2()
82+
// LLVM: %[[TMP1:.*]] = alloca i32
83+
// LLVM: store i32 0, ptr %[[TMP1]]
84+
// LLVM: %[[TMP2:.*]] = load i32, ptr %[[TMP1]]
85+
// LLVM: ret i32 %[[TMP2]]
86+
87+
// OGCG: define{{.*}} i32 @test2()
88+
// OGCG: ret i32 0
89+
90+
const char test3_c[] = {1, 2, 3, 0};
91+
92+
int test3(void) {
93+
return __builtin_constant_p(test3_c);
94+
}
95+
96+
// CIR: cir.func {{.*}} @test3() -> !s32i
97+
// CIR: %[[TMP1:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
98+
// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
99+
// CIR: cir.store %[[ZERO]], %[[TMP1]] : !s32i, !cir.ptr<!s32i>
100+
// CIR: %[[TMP2:.*]] = cir.load %[[TMP1]] : !cir.ptr<!s32i>, !s32i
101+
// CIR: cir.return %[[TMP2]] : !s32i
102+
103+
// LLVM: define{{.*}} i32 @test3()
104+
// LLVM: %[[TMP1:.*]] = alloca i32
105+
// LLVM: store i32 0, ptr %[[TMP1]]
106+
// LLVM: %[[TMP2:.*]] = load i32, ptr %[[TMP1]]
107+
// LLVM: ret i32 %[[TMP2]]
108+
109+
// OGCG: define{{.*}} i32 @test3()
110+
// OGCG: ret i32 0
111+
112+
inline char test4_i(const char *x) {
113+
return x[1];
114+
}
115+
116+
int test4(void) {
117+
return __builtin_constant_p(test4_i(test3_c));
118+
}
119+
120+
// CIR: cir.func {{.*}} @test4() -> !s32i
121+
// CIR: %[[TMP1:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
122+
// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
123+
// CIR: cir.store %[[ZERO]], %[[TMP1]] : !s32i, !cir.ptr<!s32i>
124+
// CIR: %[[TMP2:.*]] = cir.load %[[TMP1]] : !cir.ptr<!s32i>, !s32i
125+
// CIR: cir.return %[[TMP2]] : !s32i
126+
127+
// LLVM: define{{.*}} i32 @test4()
128+
// LLVM: %[[TMP1:.*]] = alloca i32
129+
// LLVM: store i32 0, ptr %[[TMP1]]
130+
// LLVM: %[[TMP2:.*]] = load i32, ptr %[[TMP1]]
131+
// LLVM: ret i32 %[[TMP2]]
132+
133+
// OGCG: define{{.*}} i32 @test4()
134+
// OGCG: ret i32 0
135+
136+
/* --- Constant global variables */
137+
138+
const int c = 42;
139+
140+
int test5(void) {
141+
return __builtin_constant_p(c);
142+
}
143+
144+
// CIR: cir.func {{.*}} @test5() -> !s32i
145+
// CIR: %[[TMP1:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
146+
// CIR: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i
147+
// CIR: cir.store %[[ONE]], %[[TMP1]] : !s32i, !cir.ptr<!s32i>
148+
// CIR: %[[TMP2:.*]] = cir.load %[[TMP1]] : !cir.ptr<!s32i>, !s32i
149+
// CIR: cir.return %[[TMP2]] : !s32i
150+
151+
// LLVM: define{{.*}} i32 @test5()
152+
// LLVM: %[[TMP1:.*]] = alloca i32
153+
// LLVM: store i32 1, ptr %[[TMP1]]
154+
// LLVM: %[[TMP2:.*]] = load i32, ptr %[[TMP1]]
155+
// LLVM: ret i32 %[[TMP2]]
156+
157+
// OGCG: define{{.*}} i32 @test5()
158+
// OGCG: ret i32 1
159+
160+
/* --- Array types */
161+
162+
int arr[] = { 1, 2, 3 };
163+
164+
int test6(void) {
165+
return __builtin_constant_p(arr[2]);
166+
}
167+
168+
// CIR: cir.func {{.*}} @test6() -> !s32i
169+
// CIR: %[[TWO:.*]] = cir.const #cir.int<2> : !s32i
170+
// CIR: %[[ARR:.*]] = cir.get_global @arr : !cir.ptr<!cir.array<!s32i x 3>>
171+
// CIR: %[[ARR_PTR:.*]] = cir.cast array_to_ptrdecay %[[ARR]] : !cir.ptr<!cir.array<!s32i x 3>> -> !cir.ptr<!s32i>
172+
// CIR: %[[ELE_PTR:.*]] = cir.ptr_stride %[[ARR_PTR]], %[[TWO]] : (!cir.ptr<!s32i>, !s32i) -> !cir.ptr<!s32i>
173+
// CIR: %[[ELE:.*]] = cir.load{{.*}} %[[ELE_PTR]] : !cir.ptr<!s32i>, !s32i
174+
// CIR: %[[IS_CONSTANT:.*]] = cir.is_constant %[[ELE]] : !s32i -> !cir.bool
175+
176+
// LLVM: define {{.*}} i32 @test6()
177+
// LLVM: %[[TMP1:.*]] = load i32, ptr getelementptr inbounds nuw (i8, ptr @arr, i64 8)
178+
// LLVM: %[[TMP2:.*]] = call i1 @llvm.is.constant.i32(i32 %[[TMP1]])
179+
180+
// OGCG: define {{.*}} i32 @test6()
181+
// OGCG: %[[TMP1:.*]] = load i32, ptr getelementptr inbounds ([3 x i32], ptr @arr, i64 0, i64 2)
182+
// OGCG: %[[TMP2:.*]] = call i1 @llvm.is.constant.i32(i32 %[[TMP1]])
183+
184+
const int c_arr[] = { 1, 2, 3 };
185+
186+
int test7(void) {
187+
return __builtin_constant_p(c_arr[2]);
188+
}
189+
190+
// CIR: cir.func {{.*}} @test7() -> !s32i
191+
// CIR: %[[TMP1:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
192+
// CIR: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i
193+
// CIR: cir.store %[[ONE]], %[[TMP1]] : !s32i, !cir.ptr<!s32i>
194+
// CIR: %[[TMP2:.*]] = cir.load %[[TMP1]] : !cir.ptr<!s32i>, !s32i
195+
// CIR: cir.return %[[TMP2]] : !s32i
196+
197+
// LLVM: define{{.*}} i32 @test7()
198+
// LLVM: %[[TMP1:.*]] = alloca i32
199+
// LLVM: store i32 1, ptr %[[TMP1]]
200+
// LLVM: %[[TMP2:.*]] = load i32, ptr %[[TMP1]]
201+
// LLVM: ret i32 %[[TMP2]]
202+
203+
// OGCG: define{{.*}} i32 @test7()
204+
// OGCG: ret i32 1
205+
206+
int test8(void) {
207+
return __builtin_constant_p(c_arr);
208+
}
209+
210+
// CIR: cir.func {{.*}} @test8() -> !s32i
211+
// CIR: %[[TMP1:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
212+
// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
213+
// CIR: cir.store %[[ZERO]], %[[TMP1]] : !s32i, !cir.ptr<!s32i>
214+
// CIR: %[[TMP2:.*]] = cir.load %[[TMP1]] : !cir.ptr<!s32i>, !s32i
215+
// CIR: cir.return %[[TMP2]] : !s32i
216+
217+
// LLVM: define{{.*}} i32 @test8()
218+
// LLVM: %[[TMP1:.*]] = alloca i32
219+
// LLVM: store i32 0, ptr %[[TMP1]]
220+
// LLVM: %[[TMP2:.*]] = load i32, ptr %[[TMP1]]
221+
// LLVM: ret i32 %[[TMP2]]
222+
223+
// OGCG: define{{.*}} i32 @test8()
224+
// OGCG: ret i32 0
225+
226+
/* --- Function pointers */
227+
228+
int test9(void) {
229+
return __builtin_constant_p(&test9);
230+
}
231+
232+
// CIR: cir.func {{.*}} @test9() -> !s32i
233+
// CIR: %[[TMP1:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
234+
// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
235+
// CIR: cir.store %[[ZERO]], %[[TMP1]] : !s32i, !cir.ptr<!s32i>
236+
// CIR: %[[TMP2:.*]] = cir.load %[[TMP1]] : !cir.ptr<!s32i>, !s32i
237+
// CIR: cir.return %[[TMP2]] : !s32i
238+
239+
// LLVM: define{{.*}} i32 @test9()
240+
// LLVM: %[[TMP1:.*]] = alloca i32
241+
// LLVM: store i32 0, ptr %[[TMP1]]
242+
// LLVM: %[[TMP2:.*]] = load i32, ptr %[[TMP1]]
243+
// LLVM: ret i32 %[[TMP2]]
244+
245+
// OGCG: define{{.*}} i32 @test9()
246+
// OGCG: ret i32 0
247+
248+
int test10(void) {
249+
return __builtin_constant_p(&test10 != 0);
250+
}
251+
252+
// CIR: cir.func {{.*}} @test10() -> !s32i
253+
// CIR: %[[TMP1:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
254+
// CIR: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i
255+
// CIR: cir.store %[[ONE]], %[[TMP1]] : !s32i, !cir.ptr<!s32i>
256+
// CIR: %[[TMP2:.*]] = cir.load %[[TMP1]] : !cir.ptr<!s32i>, !s32i
257+
// CIR: cir.return %[[TMP2]] : !s32i
258+
259+
// LLVM: define{{.*}} i32 @test10()
260+
// LLVM: %[[TMP1:.*]] = alloca i32
261+
// LLVM: store i32 1, ptr %[[TMP1]]
262+
// LLVM: %[[TMP2:.*]] = load i32, ptr %[[TMP1]]
263+
// LLVM: ret i32 %[[TMP2]]
264+
265+
// OGCG: define{{.*}} i32 @test10()
266+
// OGCG: ret i32 1
267+
268+
int test11_f(void);
269+
void test11(void) {
270+
int a, b;
271+
(void)__builtin_constant_p((a = b, test11_f()));
272+
}
273+
274+
// CIR: cir.func {{.*}} @test11()
275+
// CIR-NOT: call {{.*}}test11_f
276+
277+
// LLVM: define{{.*}} void @test11()
278+
// LLVM-NOT: call {{.*}}test11_f
279+
280+
// OGCG: define{{.*}} void @test11()
281+
// OGCG-NOT: call {{.*}}test11_f

0 commit comments

Comments
 (0)