[CIR] Add MemChrOp for __builtin_char_memchr and __builtin_memchr (#175234)

This PR adds support for the memchr builtin functions:

## Changes

- Define `CIR_MemChrOp` (`cir.libc.memchr`) operation in CIROps.td
- Add builtin handling for `__builtin_char_memchr` and
`__builtin_memchr` in CIRGenBuiltin.cpp
- Add LLVM lowering to call the `memchr` library function
- Add CodeGen and IR tests with CIR, LLVM, and OGCG checks

The operation searches for a pattern byte in a memory region and returns
a pointer to the first occurrence or null.
This commit is contained in:
adams381 2026-01-29 16:03:37 -06:00 committed by GitHub
parent dd90057d9e
commit 501b7e9cfb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 112 additions and 1 deletions

View File

@ -6242,4 +6242,35 @@ def CIR_CpuIdOp : CIR_Op<"cpuid"> {
}];
}
//===----------------------------------------------------------------------===//
// MemChrOp
//===----------------------------------------------------------------------===//
def CIR_MemChrOp : CIR_Op<"libc.memchr"> {
let summary = "libc's `memchr`";
let description = [{
Search for `pattern` in data range from `src` to `src` + `len`.
`len` provides a bound to the search in `src`. `result` is a pointer to
found `pattern` or a null pointer.
Examples:
```mlir
%p = cir.libc.memchr(%src, %pattern, %len)
```
}];
let arguments = (ins
Arg<CIR_VoidPtrType, "", [MemRead]>:$src,
CIR_SInt32:$pattern,
CIR_UInt64:$len
);
let results = (outs CIR_VoidPtrType:$result);
let assemblyFormat = [{
`(` $src `,` $pattern `,` $len `)` attr-dict
}];
}
#endif // CLANG_CIR_DIALECT_IR_CIROPS_TD

View File

@ -1287,12 +1287,22 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
case Builtin::BIbcopy:
case Builtin::BI__builtin_bcopy:
return errorBuiltinNYI(*this, e, builtinID);
case Builtin::BI__builtin_char_memchr:
case Builtin::BI__builtin_memchr: {
Address srcPtr = emitPointerWithAlignment(e->getArg(0));
mlir::Value src =
builder.createBitcast(srcPtr.getPointer(), builder.getVoidPtrTy());
mlir::Value pattern = emitScalarExpr(e->getArg(1));
mlir::Value len = emitScalarExpr(e->getArg(2));
mlir::Value res = cir::MemChrOp::create(builder, getLoc(e->getExprLoc()),
src, pattern, len);
return RValue::get(res);
}
case Builtin::BImemcpy:
case Builtin::BI__builtin_memcpy:
case Builtin::BImempcpy:
case Builtin::BI__builtin_mempcpy:
case Builtin::BI__builtin_memcpy_inline:
case Builtin::BI__builtin_char_memchr:
case Builtin::BI__builtin___memcpy_chk:
case Builtin::BI__builtin_objc_memmove_collectable:
case Builtin::BI__builtin___memmove_chk:

View File

@ -4463,6 +4463,26 @@ mlir::LogicalResult CIRToLLVMCpuIdOpLowering::matchAndRewrite(
return mlir::success();
}
mlir::LogicalResult CIRToLLVMMemChrOpLowering::matchAndRewrite(
cir::MemChrOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
mlir::Type srcTy = getTypeConverter()->convertType(op.getSrc().getType());
mlir::Type patternTy =
getTypeConverter()->convertType(op.getPattern().getType());
mlir::Type lenTy = getTypeConverter()->convertType(op.getLen().getType());
auto fnTy =
mlir::LLVM::LLVMFunctionType::get(llvmPtrTy, {srcTy, patternTy, lenTy},
/*isVarArg=*/false);
llvm::StringRef fnName = "memchr";
createLLVMFuncOpIfNotExist(rewriter, op, fnName, fnTy);
rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
op, mlir::TypeRange{llvmPtrTy}, fnName,
mlir::ValueRange{adaptor.getSrc(), adaptor.getPattern(),
adaptor.getLen()});
return mlir::success();
}
std::unique_ptr<mlir::Pass> createConvertCIRToLLVMPass() {
return std::make_unique<ConvertCIRToLLVMPass>();
}

View File

@ -0,0 +1,38 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
void *test_char_memchr(const char arg[32]) {
return __builtin_char_memchr(arg, 123, 32);
}
// CIR-LABEL: @test_char_memchr
// CIR: %[[PATTERN:.*]] = cir.const #cir.int<123> : !s32i
// CIR: %[[LEN:.*]] = cir.const #cir.int<32> : !u64i
// CIR: {{%.*}} = cir.libc.memchr({{%.*}}, %[[PATTERN]], %[[LEN]])
// LLVM-LABEL: @test_char_memchr
// LLVM: call ptr @memchr(ptr %{{.*}}, i32 123, i64 32)
// LLVM: ret ptr
// OGCG-LABEL: @test_char_memchr
// OGCG: call ptr @memchr(ptr noundef %{{.*}}, i32 noundef 123, i64 noundef 32)
// OGCG: ret ptr
void *test_memchr(const void *ptr, int val, unsigned long size) {
return __builtin_memchr(ptr, val, size);
}
// CIR-LABEL: @test_memchr
// CIR: {{%.*}} = cir.libc.memchr({{%.*}}, {{%.*}}, {{%.*}})
// LLVM-LABEL: @test_memchr
// LLVM: call ptr @memchr(ptr %{{.*}}, i32 %{{.*}}, i64 %{{.*}})
// LLVM: ret ptr
// OGCG-LABEL: @test_memchr
// OGCG: call ptr @memchr(ptr noundef %{{.*}}, i32 noundef %{{.*}}, i64 noundef %{{.*}})
// OGCG: ret ptr

View File

@ -0,0 +1,12 @@
// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
!voidptr = !cir.ptr<!cir.void>
!s32i = !cir.int<s, 32>
!u64i = !cir.int<u, 64>
module {
cir.func @f(%src : !voidptr, %pattern : !s32i, %len : !u64i) -> !voidptr {
// CHECK: cir.libc.memchr
%ptr = cir.libc.memchr(%src, %pattern, %len)
cir.return %ptr : !voidptr
}
}