Skip to content

Commit 5209668

Browse files
authored
[CIR] Upstream pointer arithmetic support (#138041)
This adds support for explicit pointer arithmetic, including unary increment and decrement of pointer values.
1 parent 49a5dd3 commit 5209668

File tree

3 files changed

+177
-5
lines changed

3 files changed

+177
-5
lines changed

clang/lib/CIR/CodeGen/CIRGenBuilder.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,14 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
185185
}
186186
bool isInt(mlir::Type i) { return mlir::isa<cir::IntType>(i); }
187187

188+
//
189+
// Constant creation helpers
190+
// -------------------------
191+
//
192+
cir::ConstantOp getSInt32(int32_t c, mlir::Location loc) {
193+
return getConstantInt(loc, getSInt32Ty(), c);
194+
}
195+
188196
// Creates constant nullptr for pointer type ty.
189197
cir::ConstantOp getNullPtr(mlir::Type ty, mlir::Location loc) {
190198
assert(!cir::MissingFeatures::targetCodeGenInfoGetNullPointer());

clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp

Lines changed: 92 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -388,9 +388,26 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
388388
// NOTE(CIR): clang calls CreateAdd but folds this to a unary op
389389
value = emitUnaryOp(e, kind, input, /*nsw=*/false);
390390
}
391-
} else if (isa<PointerType>(type)) {
392-
cgf.cgm.errorNYI(e->getSourceRange(), "Unary inc/dec pointer");
393-
return {};
391+
} else if (const PointerType *ptr = type->getAs<PointerType>()) {
392+
QualType type = ptr->getPointeeType();
393+
if (cgf.getContext().getAsVariableArrayType(type)) {
394+
// VLA types don't have constant size.
395+
cgf.cgm.errorNYI(e->getSourceRange(), "Pointer arithmetic on VLA");
396+
return {};
397+
} else if (type->isFunctionType()) {
398+
// Arithmetic on function pointers (!) is just +-1.
399+
cgf.cgm.errorNYI(e->getSourceRange(),
400+
"Pointer arithmetic on function pointer");
401+
return {};
402+
} else {
403+
// For everything else, we can just do a simple increment.
404+
mlir::Location loc = cgf.getLoc(e->getSourceRange());
405+
CIRGenBuilderTy &builder = cgf.getBuilder();
406+
int amount = (isInc ? 1 : -1);
407+
mlir::Value amt = builder.getSInt32(amount, loc);
408+
assert(!cir::MissingFeatures::sanitizers());
409+
value = builder.createPtrStride(loc, value, amt);
410+
}
394411
} else if (type->isVectorType()) {
395412
cgf.cgm.errorNYI(e->getSourceRange(), "Unary inc/dec vector");
396413
return {};
@@ -1152,8 +1169,78 @@ getUnwidenedIntegerType(const ASTContext &astContext, const Expr *e) {
11521169
static mlir::Value emitPointerArithmetic(CIRGenFunction &cgf,
11531170
const BinOpInfo &op,
11541171
bool isSubtraction) {
1155-
cgf.cgm.errorNYI(op.loc, "pointer arithmetic");
1156-
return {};
1172+
// Must have binary (not unary) expr here. Unary pointer
1173+
// increment/decrement doesn't use this path.
1174+
const BinaryOperator *expr = cast<BinaryOperator>(op.e);
1175+
1176+
mlir::Value pointer = op.lhs;
1177+
Expr *pointerOperand = expr->getLHS();
1178+
mlir::Value index = op.rhs;
1179+
Expr *indexOperand = expr->getRHS();
1180+
1181+
// In the case of subtraction, the FE has ensured that the LHS is always the
1182+
// pointer. However, addition can have the pointer on either side. We will
1183+
// always have a pointer operand and an integer operand, so if the LHS wasn't
1184+
// a pointer, we need to swap our values.
1185+
if (!isSubtraction && !mlir::isa<cir::PointerType>(pointer.getType())) {
1186+
std::swap(pointer, index);
1187+
std::swap(pointerOperand, indexOperand);
1188+
}
1189+
assert(mlir::isa<cir::PointerType>(pointer.getType()) &&
1190+
"Need a pointer operand");
1191+
assert(mlir::isa<cir::IntType>(index.getType()) && "Need an integer operand");
1192+
1193+
// Some versions of glibc and gcc use idioms (particularly in their malloc
1194+
// routines) that add a pointer-sized integer (known to be a pointer value)
1195+
// to a null pointer in order to cast the value back to an integer or as
1196+
// part of a pointer alignment algorithm. This is undefined behavior, but
1197+
// we'd like to be able to compile programs that use it.
1198+
//
1199+
// Normally, we'd generate a GEP with a null-pointer base here in response
1200+
// to that code, but it's also UB to dereference a pointer created that
1201+
// way. Instead (as an acknowledged hack to tolerate the idiom) we will
1202+
// generate a direct cast of the integer value to a pointer.
1203+
//
1204+
// The idiom (p = nullptr + N) is not met if any of the following are true:
1205+
//
1206+
// The operation is subtraction.
1207+
// The index is not pointer-sized.
1208+
// The pointer type is not byte-sized.
1209+
//
1210+
if (BinaryOperator::isNullPointerArithmeticExtension(
1211+
cgf.getContext(), op.opcode, expr->getLHS(), expr->getRHS()))
1212+
return cgf.getBuilder().createIntToPtr(index, pointer.getType());
1213+
1214+
// Differently from LLVM codegen, ABI bits for index sizes is handled during
1215+
// LLVM lowering.
1216+
1217+
// If this is subtraction, negate the index.
1218+
if (isSubtraction)
1219+
index = cgf.getBuilder().createNeg(index);
1220+
1221+
assert(!cir::MissingFeatures::sanitizers());
1222+
1223+
const PointerType *pointerType =
1224+
pointerOperand->getType()->getAs<PointerType>();
1225+
if (!pointerType) {
1226+
cgf.cgm.errorNYI("Objective-C:pointer arithmetic with non-pointer type");
1227+
return nullptr;
1228+
}
1229+
1230+
QualType elementType = pointerType->getPointeeType();
1231+
if (cgf.getContext().getAsVariableArrayType(elementType)) {
1232+
cgf.cgm.errorNYI("variable array type");
1233+
return nullptr;
1234+
}
1235+
1236+
if (elementType->isVoidType() || elementType->isFunctionType()) {
1237+
cgf.cgm.errorNYI("void* or function pointer arithmetic");
1238+
return nullptr;
1239+
}
1240+
1241+
assert(!cir::MissingFeatures::sanitizers());
1242+
return cgf.getBuilder().create<cir::PtrStrideOp>(
1243+
cgf.getLoc(op.e->getExprLoc()), pointer.getType(), pointer, index);
11571244
}
11581245

11591246
mlir::Value ScalarExprEmitter::emitMul(const BinOpInfo &ops) {

clang/test/CIR/CodeGen/pointers.cpp

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s
3+
4+
// Should generate basic pointer arithmetics.
5+
void foo(int *iptr, char *cptr, unsigned ustride) {
6+
iptr + 2;
7+
// CHECK: %[[#STRIDE:]] = cir.const #cir.int<2> : !s32i
8+
// CHECK: cir.ptr_stride(%{{.+}} : !cir.ptr<!s32i>, %[[#STRIDE]] : !s32i), !cir.ptr<!s32i>
9+
cptr + 3;
10+
// CHECK: %[[#STRIDE:]] = cir.const #cir.int<3> : !s32i
11+
// CHECK: cir.ptr_stride(%{{.+}} : !cir.ptr<!s8i>, %[[#STRIDE]] : !s32i), !cir.ptr<!s8i>
12+
iptr - 2;
13+
// CHECK: %[[#STRIDE:]] = cir.const #cir.int<2> : !s32i
14+
// CHECK: %[[#NEGSTRIDE:]] = cir.unary(minus, %[[#STRIDE]]) : !s32i, !s32i
15+
// CHECK: cir.ptr_stride(%{{.+}} : !cir.ptr<!s32i>, %[[#NEGSTRIDE]] : !s32i), !cir.ptr<!s32i>
16+
cptr - 3;
17+
// CHECK: %[[#STRIDE:]] = cir.const #cir.int<3> : !s32i
18+
// CHECK: %[[#NEGSTRIDE:]] = cir.unary(minus, %[[#STRIDE]]) : !s32i, !s32i
19+
// CHECK: cir.ptr_stride(%{{.+}} : !cir.ptr<!s8i>, %[[#NEGSTRIDE]] : !s32i), !cir.ptr<!s8i>
20+
iptr + ustride;
21+
// CHECK: %[[#STRIDE:]] = cir.load %{{.+}} : !cir.ptr<!u32i>, !u32i
22+
// CHECK: cir.ptr_stride(%{{.+}} : !cir.ptr<!s32i>, %[[#STRIDE]] : !u32i), !cir.ptr<!s32i>
23+
24+
// Must convert unsigned stride to a signed one.
25+
iptr - ustride;
26+
// CHECK: %[[#STRIDE:]] = cir.load %{{.+}} : !cir.ptr<!u32i>, !u32i
27+
// CHECK: %[[#SIGNSTRIDE:]] = cir.cast(integral, %[[#STRIDE]] : !u32i), !s32i
28+
// CHECK: %[[#NEGSTRIDE:]] = cir.unary(minus, %[[#SIGNSTRIDE]]) : !s32i, !s32i
29+
// CHECK: cir.ptr_stride(%{{.+}} : !cir.ptr<!s32i>, %[[#NEGSTRIDE]] : !s32i), !cir.ptr<!s32i>
30+
31+
4 + iptr;
32+
// CHECK: %[[#STRIDE:]] = cir.const #cir.int<4> : !s32i
33+
// CHECK: cir.ptr_stride(%{{.+}} : !cir.ptr<!s32i>, %[[#STRIDE]] : !s32i), !cir.ptr<!s32i>
34+
35+
iptr++;
36+
// CHECK: %[[#STRIDE:]] = cir.const #cir.int<1> : !s32i
37+
// CHECK: cir.ptr_stride(%{{.+}} : !cir.ptr<!s32i>, %[[#STRIDE]] : !s32i), !cir.ptr<!s32i>
38+
39+
iptr--;
40+
// CHECK: %[[#STRIDE:]] = cir.const #cir.int<-1> : !s32i
41+
// CHECK: cir.ptr_stride(%{{.+}} : !cir.ptr<!s32i>, %[[#STRIDE]] : !s32i), !cir.ptr<!s32i>
42+
}
43+
44+
void testPointerSubscriptAccess(int *ptr) {
45+
// CHECK: testPointerSubscriptAccess
46+
ptr[1];
47+
// CHECK: %[[#STRIDE:]] = cir.const #cir.int<1> : !s32i
48+
// CHECK: %[[#PTR:]] = cir.load %{{.+}} : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i>
49+
// CHECK: cir.ptr_stride(%[[#PTR]] : !cir.ptr<!s32i>, %[[#STRIDE]] : !s32i), !cir.ptr<!s32i>
50+
}
51+
52+
void testPointerMultiDimSubscriptAccess(int **ptr) {
53+
// CHECK: testPointerMultiDimSubscriptAccess
54+
ptr[1][2];
55+
// CHECK: %[[#STRIDE2:]] = cir.const #cir.int<2> : !s32i
56+
// CHECK: %[[#STRIDE1:]] = cir.const #cir.int<1> : !s32i
57+
// CHECK: %[[#PTR1:]] = cir.load %{{.+}} : !cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>, !cir.ptr<!cir.ptr<!s32i>>
58+
// CHECK: %[[#PTR2:]] = cir.ptr_stride(%[[#PTR1]] : !cir.ptr<!cir.ptr<!s32i>>, %[[#STRIDE1]] : !s32i), !cir.ptr<!cir.ptr<!s32i>>
59+
// CHECK: %[[#PTR3:]] = cir.load %[[#PTR2]] : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i>
60+
// CHECK: cir.ptr_stride(%[[#PTR3]] : !cir.ptr<!s32i>, %[[#STRIDE2]] : !s32i), !cir.ptr<!s32i>
61+
}
62+
63+
// This test is meant to verify code that handles the 'p = nullptr + n' idiom
64+
// used by some versions of glibc and gcc. This is undefined behavior but
65+
// it is intended there to act like a conversion from a pointer-sized integer
66+
// to a pointer, and we would like to tolerate that.
67+
68+
#define NULLPTRINT ((int*)0)
69+
70+
// This should get the inttoptr instruction.
71+
int *testGnuNullPtrArithmetic(unsigned n) {
72+
// CHECK: testGnuNullPtrArithmetic
73+
return NULLPTRINT + n;
74+
// CHECK: %[[NULLPTR:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!s32i>
75+
// CHECK: %[[N:.*]] = cir.load %{{.*}} : !cir.ptr<!u32i>, !u32i
76+
// CHECK: %[[RESULT:.*]] = cir.ptr_stride(%[[NULLPTR]] : !cir.ptr<!s32i>, %[[N]] : !u32i), !cir.ptr<!s32i>
77+
}

0 commit comments

Comments
 (0)