[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:
parent
dd90057d9e
commit
501b7e9cfb
@ -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
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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>();
|
||||
}
|
||||
|
||||
38
clang/test/CIR/CodeGen/builtin-memchr.c
Normal file
38
clang/test/CIR/CodeGen/builtin-memchr.c
Normal 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
|
||||
12
clang/test/CIR/IR/libc-memchr.cir
Normal file
12
clang/test/CIR/IR/libc-memchr.cir
Normal 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
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user