https://github.com/erichkeane created https://github.com/llvm/llvm-project/pull/184254
As a GNU extension, clang supports math on void* and function pointers in C mode only. From a CIR perspective, it makes sense to leave these types in the IR, since it might be useful to do analysis. During lowering, we already properly lower these to a size-1 element, so there is no changes that need to happen besides letting this get through CIR generation. This patch does that, plus adds some tests. >From fe1a005f567652292ac1790af9e2212d2da77c0c Mon Sep 17 00:00:00 2001 From: erichkeane <[email protected]> Date: Mon, 2 Mar 2026 14:46:51 -0800 Subject: [PATCH] [CIR] Implement func-ptr/void-ptr addition/subtraction/inc/dec. As a GNU extension, clang supports math on void* and function pointers in C mode only. From a CIR perspective, it makes sense to leave these types in the IR, since it might be useful to do analysis. During lowering, we already properly lower these to a size-1 element, so there is no changes that need to happen besides letting this get through CIR generation. This patch does that, plus adds some tests. --- clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 10 -- clang/test/CIR/CodeGen/gnu-ptr-math.c | 156 +++++++++++++++++++++ 2 files changed, 156 insertions(+), 10 deletions(-) create mode 100644 clang/test/CIR/CodeGen/gnu-ptr-math.c diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 03c8369753f35..411f973118252 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -676,11 +676,6 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { // VLA types don't have constant size. cgf.cgm.errorNYI(e->getSourceRange(), "Pointer arithmetic on VLA"); return {}; - } else if (type->isFunctionType()) { - // Arithmetic on function pointers (!) is just +-1. - cgf.cgm.errorNYI(e->getSourceRange(), - "Pointer arithmetic on function pointer"); - return {}; } else { // For everything else, we can just do a simple increment. mlir::Location loc = cgf.getLoc(e->getSourceRange()); @@ -1812,11 +1807,6 @@ static mlir::Value emitPointerArithmetic(CIRGenFunction &cgf, return nullptr; } - if (elementType->isVoidType() || elementType->isFunctionType()) { - cgf.cgm.errorNYI("void* or function pointer arithmetic"); - return nullptr; - } - assert(!cir::MissingFeatures::sanitizers()); return cir::PtrStrideOp::create(cgf.getBuilder(), cgf.getLoc(op.e->getExprLoc()), diff --git a/clang/test/CIR/CodeGen/gnu-ptr-math.c b/clang/test/CIR/CodeGen/gnu-ptr-math.c new file mode 100644 index 0000000000000..e5b811a1d42fd --- /dev/null +++ b/clang/test/CIR/CodeGen/gnu-ptr-math.c @@ -0,0 +1,156 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2> %t-before.cir +// RUN: FileCheck %s --input-file=%t-before.cir --check-prefixes=CIR +// RUN: FileCheck %s --input-file=%t.cir --check-prefixes=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - | FileCheck %s --check-prefixes=LLVM,LLVM_CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefixes=LLVM,LLVM_OG + +typedef void *vptr; +typedef int(*fptr)(int, double); + +vptr vptr_add(vptr p) { + // CIR-LABEL: vptr_add + // CIR: %[[ARG:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["p", init] + // CIR: %[[RET:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["__retval"] + // CIR: %[[ARG_LOAD:.*]] = cir.load {{.*}}%[[ARG]] : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void> + // CIR: %[[OFFSET:.*]] = cir.const #cir.int<3> : !s32i + // CIR: %[[STRIDE:.*]] = cir.ptr_stride %[[ARG_LOAD]], %[[OFFSET]] + // CIR: cir.store {{.*}}%[[STRIDE]], %[[RET]] + + // LLVM-LABEL: vptr_add + // LLVM: %[[ARG:.*]] = alloca ptr + // LLVM_CIR: %[[RET:.*]] = alloca ptr + // LLVM: %[[ARG_LOAD:.*]] = load ptr, ptr %[[ARG]] + // LLVM: %[[STRIDE:.*]] = getelementptr {{.*}}i8, ptr %[[ARG_LOAD]], i64 3 + // LLVM_CIR: store ptr %[[STRIDE:.*]], ptr %[[RET]] + // LLVM_OG: ret ptr %[[STRIDE]] + return p + 3; +} + +vptr vptr_sub(vptr p) { + // CIR-LABEL: vptr_sub + // CIR: %[[ARG:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["p", init] + // CIR: %[[RET:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["__retval"] + // CIR: %[[ARG_LOAD:.*]] = cir.load {{.*}}%[[ARG]] : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void> + // CIR: %[[OFFSET:.*]] = cir.const #cir.int<-2> : !s32i + // CIR: %[[STRIDE:.*]] = cir.ptr_stride %[[ARG_LOAD]], %[[OFFSET]] + // CIR: cir.store {{.*}}%[[STRIDE]], %[[RET]] + + // LLVM-LABEL: vptr_sub + // LLVM: %[[ARG:.*]] = alloca ptr + // LLVM_CIR: %[[RET:.*]] = alloca ptr + // LLVM: %[[ARG_LOAD:.*]] = load ptr, ptr %[[ARG]] + // LLVM: %[[STRIDE:.*]] = getelementptr {{.*}}i8, ptr %[[ARG_LOAD]], i64 -2 + // LLVM_CIR: store ptr %[[STRIDE:.*]], ptr %[[RET]] + // LLVM_OG: ret ptr %[[STRIDE]] + return p - 2; +} + +vptr vptr_inc(vptr p) { + // CIR-LABEL: vptr_inc + // CIR: %[[ARG:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["p", init] + // CIR: %[[RET:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["__retval"] + // CIR: %[[ARG_LOAD:.*]] = cir.load {{.*}}%[[ARG]] : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void> + // CIR: %[[OFFSET:.*]] = cir.const #cir.int<1> : !s32i + // CIR: %[[STRIDE:.*]] = cir.ptr_stride %[[ARG_LOAD]], %[[OFFSET]] + // CIR: cir.store {{.*}}%[[STRIDE]], %[[ARG]] + + // LLVM-LABEL: vptr_inc + // LLVM: %[[ARG:.*]] = alloca ptr + // LLVM_CIR: %[[RET:.*]] = alloca ptr + // LLVM: %[[ARG_LOAD:.*]] = load ptr, ptr %[[ARG]] + // LLVM: %[[STRIDE:.*]] = getelementptr {{.*}}i8, ptr %[[ARG_LOAD]], {{.*}} 1 + // LLVM_CIR: store ptr %[[STRIDE:.*]], ptr %[[RET]] + // LLVM_OG: store ptr %[[STRIDE]], ptr %[[ARG]] + return p ++; +} +vptr vptr_dec(vptr p) { + // CIR-LABEL: vptr_dec + // CIR: %[[ARG:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["p", init] + // CIR: %[[RET:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["__retval"] + // CIR: %[[ARG_LOAD:.*]] = cir.load {{.*}}%[[ARG]] : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void> + // CIR: %[[OFFSET:.*]] = cir.const #cir.int<-1> : !s32i + // CIR: %[[STRIDE:.*]] = cir.ptr_stride %[[ARG_LOAD]], %[[OFFSET]] + // CIR: cir.store {{.*}}%[[STRIDE]], %[[RET]] + + // LLVM-LABEL: vptr_dec + // LLVM: %[[ARG:.*]] = alloca ptr + // LLVM_CIR: %[[RET:.*]] = alloca ptr + // LLVM: %[[ARG_LOAD:.*]] = load ptr, ptr %[[ARG]] + // LLVM: %[[STRIDE:.*]] = getelementptr {{.*}}i8, ptr %[[ARG_LOAD]], {{.*}} -1 + // LLVM_CIR: store ptr %[[STRIDE:.*]], ptr %[[RET]] + // LLVM_OG: ret ptr %[[STRIDE]] + return --p; +} + +fptr fptr_add(fptr p) { + // CIR-LABEL: fptr_add + // CIR: %[[ARG:.*]] = cir.alloca !cir.ptr<!cir.func<(!s32i, !cir.double) -> !s32i>>, !cir.ptr<!cir.ptr<!cir.func<(!s32i, !cir.double) -> !s32i>>>, ["p", init] + // CIR: %[[RET:.*]] = cir.alloca !cir.ptr<!cir.func<(!s32i, !cir.double) -> !s32i>>, !cir.ptr<!cir.ptr<!cir.func<(!s32i, !cir.double) -> !s32i>>>, ["__retval"] + // CIR: %[[ARG_LOAD:.*]] = cir.load {{.*}}%[[ARG]] : !cir.ptr<!cir.ptr<!cir.func<(!s32i, !cir.double) -> !s32i>>>, !cir.ptr<!cir.func<(!s32i, !cir.double) -> !s32i>> + // CIR: %[[OFFSET:.*]] = cir.const #cir.int<3> : !s32i + // CIR: %[[STRIDE:.*]] = cir.ptr_stride %[[ARG_LOAD]], %[[OFFSET]] + // CIR: cir.store {{.*}}%[[STRIDE]], %[[RET]] + + // LLVM-LABEL: fptr_add + // LLVM: %[[ARG:.*]] = alloca ptr + // LLVM-CIR: %[[RET:.*]] = alloca ptr + // LLVM: %[[ARG_LOAD:.*]] = load ptr, ptr %[[ARG]] + // LLVM: %[[STRIDE:.*]] = getelementptr {{.*}}i8, ptr %[[ARG_LOAD]], i64 3 + // LLVM_CIR: store ptr %[[STRIDE:.*]], ptr %[[RET]] + // LLVM_OG: ret ptr %[[STRIDE]] + return p + 3; +} + +fptr fptr_sub(fptr p) { + // CIR-LABEL: fptr_sub + // CIR: %[[ARG:.*]] = cir.alloca !cir.ptr<!cir.func<(!s32i, !cir.double) -> !s32i>>, !cir.ptr<!cir.ptr<!cir.func<(!s32i, !cir.double) -> !s32i>>>, ["p", init] + // CIR: %[[RET:.*]] = cir.alloca !cir.ptr<!cir.func<(!s32i, !cir.double) -> !s32i>>, !cir.ptr<!cir.ptr<!cir.func<(!s32i, !cir.double) -> !s32i>>>, ["__retval"] + // CIR: %[[ARG_LOAD:.*]] = cir.load {{.*}}%[[ARG]] : !cir.ptr<!cir.ptr<!cir.func<(!s32i, !cir.double) -> !s32i>>>, !cir.ptr<!cir.func<(!s32i, !cir.double) -> !s32i>> + // CIR: %[[OFFSET:.*]] = cir.const #cir.int<-2> : !s32i + // CIR: %[[STRIDE:.*]] = cir.ptr_stride %[[ARG_LOAD]], %[[OFFSET]] + // CIR: cir.store {{.*}}%[[STRIDE]], %[[RET]] + + // LLVM-LABEL: fptr_sub + // LLVM: %[[ARG:.*]] = alloca ptr + // LLVM_CIR: %[[RET:.*]] = alloca ptr + // LLVM: %[[ARG_LOAD:.*]] = load ptr, ptr %[[ARG]] + // LLVM: %[[STRIDE:.*]] = getelementptr {{.*}}i8, ptr %[[ARG_LOAD]], i64 -2 + // LLVM_CIR: store ptr %[[STRIDE:.*]], ptr %[[RET]] + // LLVM_OG: ret ptr %[[STRIDE]] + return p - 2; +} + +fptr fptr_inc(fptr p) { + // CIR-LABEL: fptr_inc + // CIR: %[[ARG:.*]] = cir.alloca !cir.ptr<!cir.func<(!s32i, !cir.double) -> !s32i>>, !cir.ptr<!cir.ptr<!cir.func<(!s32i, !cir.double) -> !s32i>>>, ["p", init] + // CIR: %[[RET:.*]] = cir.alloca !cir.ptr<!cir.func<(!s32i, !cir.double) -> !s32i>>, !cir.ptr<!cir.ptr<!cir.func<(!s32i, !cir.double) -> !s32i>>>, ["__retval"] + // CIR: %[[ARG_LOAD:.*]] = cir.load {{.*}}%[[ARG]] : !cir.ptr<!cir.ptr<!cir.func<(!s32i, !cir.double) -> !s32i>>>, !cir.ptr<!cir.func<(!s32i, !cir.double) -> !s32i>> + // CIR: %[[OFFSET:.*]] = cir.const #cir.int<1> : !s32i + // CIR: %[[STRIDE:.*]] = cir.ptr_stride %[[ARG_LOAD]], %[[OFFSET]] + // CIR: cir.store {{.*}}%[[STRIDE]], %[[ARG]] + + // LLVM-LABEL: fptr_inc + // LLVM: %[[ARG:.*]] = alloca ptr + // LLVM_CIR: %[[RET:.*]] = alloca ptr + // LLVM: %[[STRIDE:.*]] = getelementptr {{.*}}i8, ptr %[[ARG_LOAD]], {{.*}} 1 + // LLVM_CIR: store ptr %[[STRIDE:.*]], ptr %[[RET]] + // LLVM_OG: store ptr %[[STRIDE]], ptr %[[ARG]] + return p ++; +} +fptr fptr_dec(fptr p) { + // CIR-LABEL: fptr_dec + // CIR: %[[ARG:.*]] = cir.alloca !cir.ptr<!cir.func<(!s32i, !cir.double) -> !s32i>>, !cir.ptr<!cir.ptr<!cir.func<(!s32i, !cir.double) -> !s32i>>>, ["p", init] + // CIR: %[[RET:.*]] = cir.alloca !cir.ptr<!cir.func<(!s32i, !cir.double) -> !s32i>>, !cir.ptr<!cir.ptr<!cir.func<(!s32i, !cir.double) -> !s32i>>>, ["__retval"] + // CIR: %[[ARG_LOAD:.*]] = cir.load {{.*}}%[[ARG]] : !cir.ptr<!cir.ptr<!cir.func<(!s32i, !cir.double) -> !s32i>>>, !cir.ptr<!cir.func<(!s32i, !cir.double) -> !s32i>> + // CIR: %[[OFFSET:.*]] = cir.const #cir.int<-1> : !s32i + // CIR: %[[STRIDE:.*]] = cir.ptr_stride %[[ARG_LOAD]], %[[OFFSET]] + // CIR: cir.store {{.*}}%[[STRIDE]], %[[RET]] + + // LLVM-LABEL: fptr_dec + // LLVM: %[[ARG:.*]] = alloca ptr + // LLVM_CIR: %[[RET:.*]] = alloca ptr + // LLVM: %[[STRIDE:.*]] = getelementptr {{.*}}i8, ptr %[[ARG_LOAD]], {{.*}} -1 + // LLVM_CIR: store ptr %[[STRIDE:.*]], ptr %[[RET]] + // LLVM_OG: ret ptr %[[STRIDE]] + return --p; +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
