Author: Erich Keane Date: 2026-02-03T12:54:28-08:00 New Revision: 3db2fd8bf0f3f0726bf8a32fe59e6b131f2e1b8e
URL: https://github.com/llvm/llvm-project/commit/3db2fd8bf0f3f0726bf8a32fe59e6b131f2e1b8e DIFF: https://github.com/llvm/llvm-project/commit/3db2fd8bf0f3f0726bf8a32fe59e6b131f2e1b8e.diff LOG: [CIR] Implement 'allocsize' function/call attribute lowering (#179342) The alloc_size attribute takes the argument number(normalized to the index!) of the element size and count, for things like 'malloc' or 'calloc'. This ends up being slightly more complicated than others, as this has data that we have to decide on a format for. LLVM chooses to pack both of these 32 bit values into a single i64, but unpacks it for the purpose of input/output. The second value, the number of elements, is optional. This patch uses a DenseI32ArrayAttr to store them for the LLVMIR dialect, which gets us the packed nature, but doesn't require us doing any work to unpack it. Added: clang/test/CIR/CodeGen/alloc-size.c Modified: clang/include/clang/CIR/Dialect/IR/CIRDialect.td clang/lib/CIR/CodeGen/CIRGenCall.cpp clang/test/CIR/CodeGen/coro-task.cpp clang/test/CIR/CodeGen/new.cpp clang/test/CIR/CodeGenBuiltins/builtin_new_delete.cpp mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp mlir/lib/Target/LLVMIR/ModuleImport.cpp mlir/lib/Target/LLVMIR/ModuleTranslation.cpp mlir/test/Dialect/LLVMIR/func.mlir mlir/test/Dialect/LLVMIR/roundtrip.mlir mlir/test/Target/LLVMIR/Import/function-attributes.ll mlir/test/Target/LLVMIR/Import/instructions.ll mlir/test/Target/LLVMIR/llvmir.mlir Removed: ################################################################################ diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td index bbd9831e73a50..c06807efbb83a 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td @@ -53,6 +53,7 @@ def CIR_Dialect : Dialect { static llvm::StringRef getOperandSegmentSizesAttrName() { return "operandSegmentSizes"; } static llvm::StringRef getNoCallerSavedRegsAttrName() { return "no_caller_saved_registers"; } static llvm::StringRef getNoCallbackAttrName() { return "nocallback"; } + static llvm::StringRef getAllocSizeAttrName() { return "allocsize"; } // Note: we have to name this with the underscore instead of the dash like // traditional LLVM-IR does, because the LLVM-IR-Dialect doesn't have a way // of forming names with a dash instead of underscore in its auto-generated diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index 7a1efb2ace010..abc4dd9b3c160 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -246,11 +246,20 @@ void CIRGenModule::constructAttributeList(llvm::StringRef name, // TODO(cir): Detecting 'OptimizeNone' is done here in classic codegen, when // we figure out when to do that, we should do it here. - // TODO(cir): AllocSize attr should be done here, but it has some additional - // work with forming the correct value for it. Typically this calls into - // LLVM to set it correctly, which flattens the elem size and num-elems into - // a single value. CIR should probably represent these as two values and - // handle the combination during lowering by calling into LLVM. + + if (auto *allocSizeAttr = targetDecl->getAttr<AllocSizeAttr>()) { + unsigned size = allocSizeAttr->getElemSizeParam().getLLVMIndex(); + + if (allocSizeAttr->getNumElemsParam().isValid()) { + unsigned numElts = allocSizeAttr->getNumElemsParam().getLLVMIndex(); + attrs.set(cir::CIRDialect::getAllocSizeAttrName(), + builder.getDenseI32ArrayAttr( + {static_cast<int>(size), static_cast<int>(numElts)})); + } else { + attrs.set(cir::CIRDialect::getAllocSizeAttrName(), + builder.getDenseI32ArrayAttr({static_cast<int>(size)})); + } + } // TODO(cir): Quite a few CUDA and OpenCL attributes are added here, like // uniform-work-group-size. diff --git a/clang/test/CIR/CodeGen/alloc-size.c b/clang/test/CIR/CodeGen/alloc-size.c new file mode 100644 index 0000000000000..17b548c369217 --- /dev/null +++ b/clang/test/CIR/CodeGen/alloc-size.c @@ -0,0 +1,79 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM + +#define NULL ((void *)0) + +typedef unsigned long size_t; + +// CIR: cir.func{{.*}}@my_malloc(!s32i){{.*}} attributes {allocsize = array<i32: 0>} +extern void *my_malloc(int) __attribute__((alloc_size(1))); +// CIR: cir.func{{.*}}@my_calloc(!s32i, !s32i){{.*}} attributes {allocsize = array<i32: 0, 1>} +extern void *my_calloc(int, int) __attribute__((alloc_size(1, 2))); + +// CIR-LABEL: @call_direct +// LLVM-LABEL: @call_direct +void call_direct(void) { + my_malloc(50); + // CIR: cir.call @my_malloc(%{{.*}}) {allocsize = array<i32: 0>} + // LLVM: call ptr @my_malloc(i32{{.*}} 50) [[DIRECT_MALLOC_ATTR:#[0-9]+]] + my_calloc(1, 16); + // CIR: cir.call @my_calloc(%{{.*}}) {allocsize = array<i32: 0, 1>} + // LLVM: call ptr @my_calloc(i32{{.*}} 1, i32{{.*}} 16) [[DIRECT_CALLOC_ATTR:#[0-9]+]] +} + +extern void *(*malloc_function_pointer)(void *, int)__attribute__((alloc_size(2))); +extern void *(*calloc_function_pointer)(void *, int, int)__attribute__((alloc_size(2, 3))); + +// CIR-LABEL: @call_function_pointer +// LLVM-LABEL: @call_function_pointer +void call_function_pointer(void) { + malloc_function_pointer(NULL, 100); + // CIR: %[[MALLOC_FN_PTR_GLOBAL:.*]] = cir.get_global @malloc_function_pointer + // CIR: %[[MALLOC_FN_PTR:.+]] = cir.load{{.*}}%[[MALLOC_FN_PTR_GLOBAL]] + // CIR: cir.call %[[MALLOC_FN_PTR]]({{.*}}) {allocsize = array<i32: 1>} + // + // LLVM: %[[MALLOC_FN_PTR:.+]] = load ptr, ptr @malloc_function_pointer, align 8 + // LLVM: call ptr %[[MALLOC_FN_PTR]](ptr{{.*}} null, i32{{.*}} 100) [[INDIRECT_MALLOC_ATTR:#[0-9]+]] + calloc_function_pointer(NULL, 2, 4); + // CIR: %[[CALLOC_FN_PTR_GLOBAL:.*]] = cir.get_global @calloc_function_pointer + // CIR: %[[CALLOC_FN_PTR:.+]] = cir.load{{.*}}%[[CALLOC_FN_PTR_GLOBAL]] + // CIR: cir.call %[[CALLOC_FN_PTR]]({{.*}}) {allocsize = array<i32: 1, 2>} + // + // LLVM: %[[CALLOC_FN_PTR:.+]] = load ptr, ptr @calloc_function_pointer, align 8 + // LLVM: call ptr %[[CALLOC_FN_PTR]](ptr{{.*}} null, i32{{.*}} 2, i32{{.*}} 4) [[INDIRECT_CALLOC_ATTR:#[0-9]+]] +} + +typedef void *(__attribute__((alloc_size(3))) * my_malloc_fn_pointer_type)(void *, void *, int); +typedef void *(__attribute__((alloc_size(3, 4))) * my_calloc_fn_pointer_type)(void *, void *, int, int); +extern my_malloc_fn_pointer_type malloc_function_pointer_with_typedef; +extern my_calloc_fn_pointer_type calloc_function_pointer_with_typedef; + +// CIR-LABEL: @call_function_pointer_typedef +// LLVM-LABEL: @call_function_pointer_typedef +void call_function_pointer_typedef(void) { + malloc_function_pointer_with_typedef(NULL, NULL, 200); + // CIR: %[[INDIRECT_TYPEDEF_MALLOC_FN_PTR_GLOBAL:.*]] = cir.get_global @malloc_function_pointer_with_typedef + // CIR: %[[INDIRECT_TYPEDEF_MALLOC_FN_PTR:.+]] = cir.load{{.*}}%[[INDIRECT_TYPEDEF_MALLOC_FN_PTR_GLOBAL]] + // CIR: cir.call %[[INDIRECT_TYPEDEF_MALLOC_FN_PTR]]({{.*}}) {allocsize = array<i32: 2>} + // + // LLVM: %[[INDIRECT_TYPEDEF_MALLOC_FN_PTR:.+]] = load ptr, ptr @malloc_function_pointer_with_typedef, align 8 + // LLVM: call ptr %[[INDIRECT_TYPEDEF_MALLOC_FN_PTR]](ptr{{.*}} null, ptr{{.*}} null, i32{{.*}} 200) [[INDIRECT_TYPEDEF_MALLOC_ATTR:#[0-9]+]] + calloc_function_pointer_with_typedef(NULL, NULL, 8, 4); + // CIR: %[[INDIRECT_TYPEDEF_CALLOC_FN_PTR_GLOBAL:.*]] = cir.get_global @calloc_function_pointer_with_typedef + // CIR: %[[INDIRECT_TYPEDEF_CALLOC_FN_PTR:.+]] = cir.load{{.*}}%[[INDIRECT_TYPEDEF_CALLOC_FN_PTR_GLOBAL]] + // CIR: cir.call %[[INDIRECT_TYPEDEF_CALLOC_FN_PTR]]({{.*}}) {allocsize = array<i32: 2, 3>} + // + // LLVM: %[[INDIRECT_TYPEDEF_CALLOC_FN_PTR:.+]] = load ptr, ptr @calloc_function_pointer_with_typedef, align 8 + // LLVM: call ptr %[[INDIRECT_TYPEDEF_CALLOC_FN_PTR]](ptr{{.*}} null, ptr{{.*}} null, i32{{.*}} 8, i32{{.*}} 4) [[INDIRECT_TYPEDEF_CALLOC_ATTR:#[0-9]+]] +} + +// LLVM: attributes [[DIRECT_MALLOC_ATTR]] = { allocsize(0) } +// LLVM: attributes [[DIRECT_CALLOC_ATTR]] = { allocsize(0,1) } +// LLVM: attributes [[INDIRECT_MALLOC_ATTR]] = { allocsize(1) } +// LLVM: attributes [[INDIRECT_CALLOC_ATTR]] = { allocsize(1,2) } +// LLVM: attributes [[INDIRECT_TYPEDEF_MALLOC_ATTR]] = { allocsize(2) } +// LLVM: attributes [[INDIRECT_TYPEDEF_CALLOC_ATTR]] = { allocsize(2,3) } diff --git a/clang/test/CIR/CodeGen/coro-task.cpp b/clang/test/CIR/CodeGen/coro-task.cpp index c7072daa051e5..0a2b49345e628 100644 --- a/clang/test/CIR/CodeGen/coro-task.cpp +++ b/clang/test/CIR/CodeGen/coro-task.cpp @@ -173,7 +173,7 @@ VoidTask silly_task() { // CIR: cir.store{{.*}} %[[NullPtr]], %[[SavedFrameAddr]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>> // CIR: cir.if %[[ShouldAlloc]] { // CIR: %[[CoroSize:.*]] = cir.call @__builtin_coro_size() : () -> !u64i -// CIR: %[[AllocAddr:.*]] = cir.call @_Znwm(%[[CoroSize]]) : (!u64i) -> !cir.ptr<!void> +// CIR: %[[AllocAddr:.*]] = cir.call @_Znwm(%[[CoroSize]]) {allocsize = array<i32: 0>} : (!u64i) -> !cir.ptr<!void> // CIR: cir.store{{.*}} %[[AllocAddr]], %[[SavedFrameAddr]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>> // CIR: } // CIR: %[[Load0:.*]] = cir.load{{.*}} %[[SavedFrameAddr]] : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void> diff --git a/clang/test/CIR/CodeGen/new.cpp b/clang/test/CIR/CodeGen/new.cpp index 9f454770e3960..ed6f5b6450fb2 100644 --- a/clang/test/CIR/CodeGen/new.cpp +++ b/clang/test/CIR/CodeGen/new.cpp @@ -160,7 +160,7 @@ void test_new_with_complex_type() { // CHECK: cir.func{{.*}} @_Z26test_new_with_complex_typev // CHECK: %[[A_ADDR:.*]] = cir.alloca !cir.ptr<!cir.complex<!cir.float>>, !cir.ptr<!cir.ptr<!cir.complex<!cir.float>>>, ["a", init] // CHECK: %[[COMPLEX_SIZE:.*]] = cir.const #cir.int<8> : !u64i -// CHECK: %[[NEW_COMPLEX:.*]] = cir.call @_Znwm(%[[COMPLEX_SIZE]]) : (!u64i) -> !cir.ptr<!void> +// CHECK: %[[NEW_COMPLEX:.*]] = cir.call @_Znwm(%[[COMPLEX_SIZE]]) {allocsize = array<i32: 0>} : (!u64i) -> !cir.ptr<!void> // CHECK: %[[COMPLEX_PTR:.*]] = cir.cast bitcast %[[NEW_COMPLEX]] : !cir.ptr<!void> -> !cir.ptr<!cir.complex<!cir.float>> // CHECK: %[[COMPLEX_VAL:.*]] = cir.const #cir.const_complex<#cir.fp<1.000000e+00> : !cir.float, #cir.fp<2.000000e+00> : !cir.float> : !cir.complex<!cir.float> // CHECK: cir.store{{.*}} %[[COMPLEX_VAL]], %[[COMPLEX_PTR]] : !cir.complex<!cir.float>, !cir.ptr<!cir.complex<!cir.float>> @@ -188,7 +188,7 @@ void t_new_constant_size() { // CHECK: cir.func{{.*}} @_Z19t_new_constant_sizev() // CHECK: %[[P_ADDR:.*]] = cir.alloca !cir.ptr<!cir.double>, !cir.ptr<!cir.ptr<!cir.double>>, ["p", init] {alignment = 8 : i64} // CHECK: %[[ALLOCATION_SIZE:.*]] = cir.const #cir.int<128> : !u64i -// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void> +// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) {allocsize = array<i32: 0>} : (!u64i) -> !cir.ptr<!void> // CHECK: %[[TYPED_PTR:.*]] = cir.cast bitcast %[[RAW_PTR]] : !cir.ptr<!void> -> !cir.ptr<!cir.double> // CHECK: cir.store align(8) %[[TYPED_PTR]], %[[P_ADDR]] : !cir.ptr<!cir.double>, !cir.ptr<!cir.ptr<!cir.double>> // CHECK: cir.return @@ -217,7 +217,7 @@ void t_constant_size_nontrivial() { // CHECK: %[[P_ADDR:.*]] = cir.alloca !cir.ptr<!rec_C>, !cir.ptr<!cir.ptr<!rec_C>>, ["p", init] {alignment = 8 : i64} // CHECK: %[[NUM_ELEMENTS:.*]] = cir.const #cir.int<3> : !u64i // CHECK: %[[ALLOCATION_SIZE:.*]] = cir.const #cir.int<11> : !u64i -// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void> +// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) {allocsize = array<i32: 0>} : (!u64i) -> !cir.ptr<!void> // CHECK: %[[COOKIE_PTR_BASE:.*]] = cir.cast bitcast %[[RAW_PTR]] : !cir.ptr<!void> -> !cir.ptr<!cir.ptr<!u8i>> // CHECK: %[[COOKIE_PTR:.*]] = cir.cast bitcast %[[COOKIE_PTR_BASE]] : !cir.ptr<!cir.ptr<!u8i>> -> !cir.ptr<!u64i> // CHECK: cir.store align(8) %[[NUM_ELEMENTS]], %[[COOKIE_PTR]] : !u64i, !cir.ptr<!u64i> @@ -257,7 +257,7 @@ void t_constant_size_nontrivial2() { // CHECK: %[[P_ADDR:.*]] = cir.alloca !cir.ptr<!rec_D>, !cir.ptr<!cir.ptr<!rec_D>>, ["p", init] {alignment = 8 : i64} // CHECK: %[[NUM_ELEMENTS:.*]] = cir.const #cir.int<3> : !u64i // CHECK: %[[ALLOCATION_SIZE:.*]] = cir.const #cir.int<20> : !u64i -// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void> +// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) {allocsize = array<i32: 0>} : (!u64i) -> !cir.ptr<!void> // CHECK: %[[COOKIE_PTR_BASE:.*]] = cir.cast bitcast %[[RAW_PTR]] : !cir.ptr<!void> -> !cir.ptr<!cir.ptr<!u8i>> // CHECK: %[[COOKIE_PTR:.*]] = cir.cast bitcast %[[COOKIE_PTR_BASE]] : !cir.ptr<!cir.ptr<!u8i>> -> !cir.ptr<!u64i> // CHECK: cir.store align(8) %[[NUM_ELEMENTS]], %[[COOKIE_PTR]] : !u64i, !cir.ptr<!u64i> @@ -289,7 +289,7 @@ void t_align16_nontrivial() { // CHECK: %[[P_ADDR:.*]] = cir.alloca !cir.ptr<!rec_E>, !cir.ptr<!cir.ptr<!rec_E>>, ["p", init] {alignment = 8 : i64} // CHECK: %[[NUM_ELEMENTS:.*]] = cir.const #cir.int<2> : !u64i // CHECK: %[[ALLOCATION_SIZE:.*]] = cir.const #cir.int<48> : !u64i -// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void> +// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) {allocsize = array<i32: 0>} : (!u64i) -> !cir.ptr<!void> // CHECK: %[[COOKIE_PTR_BASE:.*]] = cir.cast bitcast %[[RAW_PTR]] : !cir.ptr<!void> -> !cir.ptr<!cir.ptr<!u8i>> // CHECK: %[[COOKIE_OFFSET:.*]] = cir.const #cir.int<8> : !s32i // CHECK: %[[COOKIE_PTR_RAW:.*]] = cir.ptr_stride %[[COOKIE_PTR_BASE]], %[[COOKIE_OFFSET]] : (!cir.ptr<!cir.ptr<!u8i>>, !s32i) -> !cir.ptr<!cir.ptr<!u8i>> @@ -327,7 +327,7 @@ void t_new_multidim_constant_size() { // CHECK: cir.func{{.*}} @_Z28t_new_multidim_constant_sizev() // CHECK: %[[P_ADDR:.*]] = cir.alloca !cir.ptr<!cir.array<!cir.array<!cir.double x 4> x 3>>, !cir.ptr<!cir.ptr<!cir.array<!cir.array<!cir.double x 4> x 3>>>, ["p", init] {alignment = 8 : i64} // CHECK: %[[ALLOCATION_SIZE:.*]] = cir.const #cir.int<192> : !u64i -// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void> +// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) {allocsize = array<i32: 0>} : (!u64i) -> !cir.ptr<!void> // CHECK: %[[TYPED_PTR:.*]] = cir.cast bitcast %[[RAW_PTR]] : !cir.ptr<!void> -> !cir.ptr<!cir.array<!cir.array<!cir.double x 4> x 3>> // CHECK: cir.store align(8) %[[TYPED_PTR]], %[[P_ADDR]] : !cir.ptr<!cir.array<!cir.array<!cir.double x 4> x 3>>, !cir.ptr<!cir.ptr<!cir.array<!cir.array<!cir.double x 4> x 3>>> // CHECK: } @@ -348,7 +348,7 @@ void t_constant_size_memset_init() { // CHECK: cir.func {{.*}} @_Z27t_constant_size_memset_initv() // CHECK: %[[ALLOCATION_SIZE:.*]] = cir.const #cir.int<64> : !u64i -// CHECK: %[[ALLOC_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void> +// CHECK: %[[ALLOC_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) {allocsize = array<i32: 0>} : (!u64i) -> !cir.ptr<!void> // CHECK: %[[ELEM_PTR:.*]] = cir.cast bitcast %[[ALLOC_PTR]] : !cir.ptr<!void> -> !cir.ptr<!s32i> // CHECK: %[[VOID_PTR:.*]] = cir.cast bitcast %[[ELEM_PTR]] : !cir.ptr<!s32i> -> !cir.ptr<!void> // CHECK: %[[ZERO:.*]] = cir.const #cir.int<0> : !u8i diff --git a/clang/test/CIR/CodeGenBuiltins/builtin_new_delete.cpp b/clang/test/CIR/CodeGenBuiltins/builtin_new_delete.cpp index d540bfcf8a36d..52f1599a75e94 100644 --- a/clang/test/CIR/CodeGenBuiltins/builtin_new_delete.cpp +++ b/clang/test/CIR/CodeGenBuiltins/builtin_new_delete.cpp @@ -9,7 +9,7 @@ void test_builtins_basic() { __builtin_operator_delete(__builtin_operator_new(4)); // CIR-LABEL: test_builtins_basic - // CIR: [[P:%.*]] = cir.call @_Znwm({{%.*}}) : (!u64i) -> !cir.ptr<!void> + // CIR: [[P:%.*]] = cir.call @_Znwm({{%.*}}) {allocsize = array<i32: 0>} : (!u64i) -> !cir.ptr<!void> // CIR: cir.call @_ZdlPv([[P]]) {{.*}}: (!cir.ptr<!void>) -> () // CIR: cir.return @@ -28,7 +28,7 @@ void test_sized_delete() { __builtin_operator_delete(__builtin_operator_new(4), 4); // CIR-LABEL: test_sized_delete - // CIR: [[P:%.*]] = cir.call @_Znwm({{%.*}}) : (!u64i) -> !cir.ptr<!void> + // CIR: [[P:%.*]] = cir.call @_Znwm({{%.*}}) {allocsize = array<i32: 0>} : (!u64i) -> !cir.ptr<!void> // CIR: cir.call @_ZdlPvm([[P]], {{%.*}}) {{.*}}: (!cir.ptr<!void>, !u64i) -> () // CIR: cir.return diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td index e2358dcf1ed4c..a03a933eed370 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td @@ -800,6 +800,7 @@ def LLVM_CallOp UnitAttr:$no_caller_saved_registers, UnitAttr:$nocallback, OptionalAttr<StrAttr>:$modular_format, OptionalAttr<ArrayAttr>:$nobuiltins, + OptionalAttr<DenseI32ArrayAttr>:$allocsize, VariadicOfVariadic<LLVM_Type, "op_bundle_sizes">:$op_bundle_operands, DenseI32ArrayAttr:$op_bundle_sizes, OptionalAttr<ArrayAttr>:$op_bundle_tags, @@ -2007,6 +2008,7 @@ def LLVM_LLVMFuncOp : LLVM_Op<"func", [ OptionalAttr<UnitAttr>:$nocallback, OptionalAttr<StrAttr>:$modular_format, OptionalAttr<ArrayAttr>:$nobuiltins, + OptionalAttr<DenseI32ArrayAttr>:$allocsize, OptionalAttr<LLVM_VecTypeHintAttr>:$vec_type_hint, OptionalAttr<DenseI32ArrayAttr>:$work_group_size_hint, OptionalAttr<DenseI32ArrayAttr>:$reqd_work_group_size, diff --git a/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h b/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h index a9b6a58b30e10..349c0f8810a47 100644 --- a/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h +++ b/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h @@ -370,6 +370,8 @@ class ModuleTranslation { } } + llvm::Attribute convertAllocsizeAttr(DenseI32ArrayAttr allocsizeAttr); + private: ModuleTranslation(Operation *module, std::unique_ptr<llvm::Module> llvmModule); diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp index 34d0de078e553..d4573060eca25 100644 --- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp +++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp @@ -999,6 +999,7 @@ void CallOp::build(OpBuilder &builder, OperationState &state, TypeRange results, /*cold=*/nullptr, /*noduplicate=*/nullptr, /*no_caller_saved_registers=*/nullptr, /*nocallback=*/nullptr, /*modular_format=*/nullptr, /*nobuiltins=*/nullptr, + /*alloc_size=*/nullptr, /*op_bundle_operands=*/{}, /*op_bundle_tags=*/{}, /*arg_attrs=*/nullptr, /*res_attrs=*/nullptr, /*access_groups=*/nullptr, /*alias_scopes=*/nullptr, @@ -1034,6 +1035,7 @@ void CallOp::build(OpBuilder &builder, OperationState &state, /*cold=*/nullptr, /*noduplicate=*/nullptr, /*no_caller_saved_registers=*/nullptr, /*nocallback=*/nullptr, /*modular_format=*/nullptr, /*nobuiltins=*/nullptr, + /*alloc_size=*/nullptr, /*op_bundle_operands=*/{}, /*op_bundle_tags=*/{}, /*arg_attrs=*/nullptr, /*res_attrs=*/nullptr, /*access_groups=*/nullptr, @@ -1055,6 +1057,7 @@ void CallOp::build(OpBuilder &builder, OperationState &state, /*cold=*/nullptr, /*noduplicate=*/nullptr, /*no_caller_saved_registers=*/nullptr, /*nocallback=*/nullptr, /*modular_format=*/nullptr, /*nobuiltins=*/nullptr, + /*alloc_size=*/nullptr, /*op_bundle_operands=*/{}, /*op_bundle_tags=*/{}, /*arg_attrs=*/nullptr, /*res_attrs=*/nullptr, /*access_groups=*/nullptr, /*alias_scopes=*/nullptr, @@ -1076,6 +1079,7 @@ void CallOp::build(OpBuilder &builder, OperationState &state, LLVMFuncOp func, /*cold=*/nullptr, /*noduplicate=*/nullptr, /*no_caller_saved_registers=*/nullptr, /*nocallback=*/nullptr, /*modular_format=*/nullptr, /*nobuiltins=*/nullptr, + /*alloc_size=*/nullptr, /*op_bundle_operands=*/{}, /*op_bundle_tags=*/{}, /*access_groups=*/nullptr, /*alias_scopes=*/nullptr, /*arg_attrs=*/nullptr, /*res_attrs=*/nullptr, diff --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp index 68c634538d92b..93a8e00d40e35 100644 --- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp @@ -456,6 +456,11 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder, noBuiltins, call, ModuleTranslation::convertNoBuiltin); } + if (llvm::Attribute attr = + moduleTranslation.convertAllocsizeAttr(callOp.getAllocsizeAttr()); + attr.isValid()) + call->addFnAttr(attr); + if (failed(moduleTranslation.convertArgAndResultAttrs(callOp, call))) return failure(); diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp index feb5011a5cc91..2c1613c890923 100644 --- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp +++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp @@ -2659,6 +2659,7 @@ static constexpr std::array kExplicitLLVMFuncOpAttributes{ StringLiteral("aarch64_pstate_sm_body"), StringLiteral("aarch64_pstate_sm_compatible"), StringLiteral("aarch64_pstate_sm_enabled"), + StringLiteral("allocsize"), StringLiteral("alwaysinline"), StringLiteral("cold"), StringLiteral("convergent"), @@ -2725,6 +2726,24 @@ static void convertNoBuiltinAttrs(MLIRContext *ctx, target.setNobuiltinsAttr(ArrayAttr::get(ctx, nbAttrs.getArrayRef())); } +template <typename OpTy> +static void convertAllocsizeAttr(MLIRContext *ctx, + const llvm::AttributeSet &attrs, OpTy target) { + llvm::Attribute attr = attrs.getAttribute(llvm::Attribute::AllocSize); + if (!attr.isValid()) + return; + + auto [elemSize, numElems] = attr.getAllocSizeArgs(); + if (numElems) { + target.setAllocsizeAttr( + DenseI32ArrayAttr::get(ctx, {static_cast<int32_t>(elemSize), + static_cast<int32_t>(*numElems)})); + } else { + target.setAllocsizeAttr( + DenseI32ArrayAttr::get(ctx, {static_cast<int32_t>(elemSize)})); + } +} + /// Converts LLVM attributes from `func` into MLIR attributes and adds them /// to `funcOp` as passthrough attributes, skipping those listed in /// `kExplicitLLVMFuncAttributes`. @@ -2794,6 +2813,7 @@ void ModuleImport::processFunctionAttributes(llvm::Function *func, funcOp.setArmPreservesZa(true); convertNoBuiltinAttrs(context, func->getAttributes().getFnAttrs(), funcOp); + convertAllocsizeAttr(context, func->getAttributes().getFnAttrs(), funcOp); llvm::Attribute attr = func->getFnAttribute(llvm::Attribute::VScaleRange); if (attr.isValid()) { @@ -3036,6 +3056,7 @@ LogicalResult ModuleImport::convertCallAttributes(llvm::CallInst *inst, op.setMemoryEffectsAttr(memAttr); convertNoBuiltinAttrs(op.getContext(), callAttrs.getFnAttrs(), op); + convertAllocsizeAttr(op.getContext(), callAttrs.getFnAttrs(), op); return convertCallBaseAttributes(inst, op); } diff --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp index e87b4a6502359..6ee9b0cc1cee7 100644 --- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp @@ -1659,6 +1659,20 @@ static void convertFunctionMemoryAttributes(LLVMFuncOp func, llvmFunc->setMemoryEffects(newMemEffects); } +llvm::Attribute +ModuleTranslation::convertAllocsizeAttr(DenseI32ArrayAttr allocSizeAttr) { + if (!allocSizeAttr || allocSizeAttr.empty()) + return llvm::Attribute{}; + + unsigned elemSize = static_cast<unsigned>(allocSizeAttr[0]); + std::optional<unsigned> numElems; + if (allocSizeAttr.size() > 1) + numElems = static_cast<unsigned>(allocSizeAttr[1]); + + return llvm::Attribute::getWithAllocSizeArgs(getLLVMContext(), elemSize, + numElems); +} + /// Converts function attributes from `func` and attaches them to `llvmFunc`. static void convertFunctionAttributes(ModuleTranslation &mod, LLVMFuncOp func, llvm::Function *llvmFunc) { @@ -1709,6 +1723,10 @@ static void convertFunctionAttributes(ModuleTranslation &mod, LLVMFuncOp func, ModuleTranslation::convertNoBuiltin); } + if (llvm::Attribute attr = mod.convertAllocsizeAttr(func.getAllocsizeAttr()); + attr.isValid()) + llvmFunc->addFnAttr(attr); + convertFunctionMemoryAttributes(func, llvmFunc); } diff --git a/mlir/test/Dialect/LLVMIR/func.mlir b/mlir/test/Dialect/LLVMIR/func.mlir index 2f1bd0eb96910..8dc7f1ddab11c 100644 --- a/mlir/test/Dialect/LLVMIR/func.mlir +++ b/mlir/test/Dialect/LLVMIR/func.mlir @@ -384,6 +384,18 @@ module { llvm.return } + llvm.func @alloc_size_one(%arg: i32, %arg2: i32, %arg3: i32, %args4: i32) attributes { allocsize = array<i32: 3>} { + // CHECK: @alloc_size_one + // CHECK-SAME: attributes {allocsize = array<i32: 3>} + llvm.return + } + + llvm.func @alloc_size_two(%arg: i32, %arg2: i32, %arg3: i32, %args4: i32) attributes { allocsize = array<i32:3, 1> } { + // CHECK: @alloc_size_two + // CHECK-SAME: attributes {allocsize = array<i32: 3, 1>} + llvm.return + } + } // ----- diff --git a/mlir/test/Dialect/LLVMIR/roundtrip.mlir b/mlir/test/Dialect/LLVMIR/roundtrip.mlir index 2d1a383274e97..a39a4e9e18a56 100644 --- a/mlir/test/Dialect/LLVMIR/roundtrip.mlir +++ b/mlir/test/Dialect/LLVMIR/roundtrip.mlir @@ -1,4 +1,4 @@ -// RUN: mlir-opt -verify-roundtrip %s +// RUN: mlir-opt -verify-roundtrip %s | FileCheck %s // CHECK-LABEL: func @baz @@ -155,6 +155,12 @@ func.func @ops(%arg0: i32, %arg1: f32, // CHECK: llvm.call @baz() {nobuiltins = ["asdf", "defg"]} : () -> () llvm.call @baz() {nobuiltins = ["asdf", "defg"]} : () -> () +// CHECK: llvm.call @baz() {allocsize = array<i32: 5>} : () -> () + llvm.call @baz() {allocsize = array<i32: 5>} : () -> () + +// CHECK: llvm.call @baz() {allocsize = array<i32: 3, 5>} : () -> () + llvm.call @baz() {allocsize = array<i32: 3, 5>} : () -> () + // Terminator operations and their successors. // // CHECK: llvm.br ^[[BB1:.*]] diff --git a/mlir/test/Target/LLVMIR/Import/function-attributes.ll b/mlir/test/Target/LLVMIR/Import/function-attributes.ll index f4a060280a95c..6348511935e0c 100644 --- a/mlir/test/Target/LLVMIR/Import/function-attributes.ll +++ b/mlir/test/Target/LLVMIR/Import/function-attributes.ll @@ -471,5 +471,17 @@ declare void @no_builtins_2() "no-builtin-asdf" "no-builtin-defg" // ----- +; CHECK-LABEL: @alloc_size_1 +; CHECK-SAME: attributes {allocsize = array<i32: 0>} +declare void @alloc_size_1(i32) allocsize(0) + +// ----- + +; CHECK-LABEL: @alloc_size_2 +; CHECK-SAME: attributes {allocsize = array<i32: 0, 1>} +declare void @alloc_size_2(i32, i32) allocsize(0, 1) + +// ----- + ; expected-warning @unknown {{'preallocated' attribute is invalid on current operation, skipping it}} declare void @test() preallocated(i32) diff --git a/mlir/test/Target/LLVMIR/Import/instructions.ll b/mlir/test/Target/LLVMIR/Import/instructions.ll index cf3962a92c46c..a72227f01716c 100644 --- a/mlir/test/Target/LLVMIR/Import/instructions.ll +++ b/mlir/test/Target/LLVMIR/Import/instructions.ll @@ -817,6 +817,30 @@ define void @call_nobuiltins_2() { ret void } + +; // ----- + +; CHECK: llvm.func @f(i32, i32) +declare void @f(i32, i32) + +; CHECK-LABEL: @call_alloc_size_1 +define void @call_alloc_size_1() { +; CHECK: llvm.call @f({{.*}}) {allocsize = array<i32: 0>} + call void @f(i32 0, i32 0) allocsize(0) + ret void +} +; // ----- + +; CHECK: llvm.func @f(i32, i32) +declare void @f(i32, i32) + +; CHECK-LABEL: @call_alloc_size_2 +define void @call_alloc_size_2() { +; CHECK: llvm.call @f({{.*}}) {allocsize = array<i32: 1, 0>} + call void @f(i32 0, i32 0) allocsize(1, 0) + ret void +} + ; // ----- ; CHECK: llvm.func @f() diff --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir index ba566e7931359..f88cbda459e80 100644 --- a/mlir/test/Target/LLVMIR/llvmir.mlir +++ b/mlir/test/Target/LLVMIR/llvmir.mlir @@ -2771,6 +2771,58 @@ llvm.func @no_builtins_call_2() { // ----- +// CHECK-LABEL: @allocsize_1 +// CHECK-SAME: #[[ATTRS:[0-9]+]] +llvm.func @allocsize_1(%arg: i32, %arg2: i32) attributes { allocsize = array<i32: 1> } { + llvm.return +} + +// CHECK: #[[ATTRS]] +// CHECK-SAME: allocsize(1) + +// ----- + +// CHECK-LABEL: @allocsize_2 +// CHECK-SAME: #[[ATTRS:[0-9]+]] +llvm.func @allocsize_2(%arg: i32, %arg2: i32) attributes { allocsize = array<i32:0, 1> } { + llvm.return +} + +// CHECK: #[[ATTRS]] +// CHECK-SAME: allocsize(0,1) + +// ----- + +llvm.func @f(i32, i32) + +// CHECK-LABEL: @allocsize_call_1 +// CHECK: call void @f({{.*}}) #[[ATTRS:[0-9]+]] +llvm.func @allocsize_call_1() { + %0 = llvm.mlir.constant(0 : i32) : i32 + llvm.call @f(%0, %0) {allocsize = array<i32: 1> } : (i32, i32) -> () + llvm.return +} + +// CHECK: #[[ATTRS]] +// CHECK-SAME: allocsize(1) + +// ----- + +llvm.func @f(i32, i32) + +// CHECK-LABEL: @allocsize_call_2 +// CHECK: call void @f({{.*}}) #[[ATTRS:[0-9]+]] +llvm.func @allocsize_call_2() { + %0 = llvm.mlir.constant(0 : i32) : i32 + llvm.call @f(%0, %0) {allocsize = array<i32: 1, 0> } : (i32, i32) -> () + llvm.return +} + +// CHECK: #[[ATTRS]] +// CHECK-SAME: allocsize(1,0) + +// ----- + llvm.func @f() // CHECK-LABEL: @convergent_call _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
