llvm-project/flang/test/Fir/convert-to-llvm-target.fir
jeanPerier 8a1ce2d6c2
[flang][codegen] Update FIR codegen to use mlir.llvm opaque pointers (#69692)
!llvm.ptr<T> typed pointers are depreciated in MLIR LLVM dialects. Flang
codegen still generated them and relied on mlir.llvm codegen to LLVM to
turn them into opaque pointers.

This patch update FIR codegen to directly emit and work with LLVM opaque
pointers.

Addresses https://github.com/llvm/llvm-project/issues/69303

- All places generating GEPs need to add an extra type argument with the
base type (the T that was previously in the llvm.ptr<T> of the base).

- llvm.alloca must also be provided the object type. In the process, I
doscovered that we were shamelessly copying all the attribute from
fir.alloca to the llvm.alloca, which makes no sense for the operand
segments. The updated code that cannot take an attribute dictionnary in
the llvm.alloca builder with opaque pointers only propagate the "pinned"
and "bindc_name" attributes to help debugging the generated IR.

- Updating all the places that rely on getting the llvm object type from
lowered llvm.ptr<T> arguments to get it from a type conversion of the
original fir types.

- Updating all the places that were generating llvm.ptr<T> types to
generate the opaque llvm.ptr type.

- Updating all the codegen tests checking generated MLIR llvm dialect.
Many tests are testing directly LLVM IR, and this change is a no-op for
those (which is expected).
2023-10-25 09:42:28 +02:00

150 lines
6.3 KiB
Plaintext

// RUN: fir-opt --split-input-file --fir-to-llvm-ir="target=x86_64-unknown-linux-gnu" %s | FileCheck %s --check-prefix INT64
// RUN: fir-opt --split-input-file --fir-to-llvm-ir="target=aarch64-unknown-linux-gnu" %s | FileCheck %s --check-prefixes INT64
// RUN: fir-opt --split-input-file --fir-to-llvm-ir="target=i386-unknown-linux-gnu" %s | FileCheck %s --check-prefixes INT32
// RUN: fir-opt --split-input-file --fir-to-llvm-ir="target=powerpc64le-unknown-linux-gn" %s | FileCheck %s --check-prefixes INT64
//=============================================================================
// SUMMARY: Tests for FIR --> LLVM MLIR conversion that *depend* on the target
//=============================================================================
// Test fir.emboxchar
func.func @test_embox(%char_array : !fir.ref<!fir.char<1,?>>) -> () {
%c10 = arith.constant 10 : i64
%box_char = fir.emboxchar %char_array, %c10 : (!fir.ref<!fir.char<1,?>>, i64) -> !fir.boxchar<1>
return
}
// INT64-LABEL: test_embox
// INT64-SAME: (%[[char_array:.*]]: !llvm.ptr)
// INT64: %[[c10:.*]] = llvm.mlir.constant(10 : i64) : i64
// INT64: %[[empty_struct:.*]] = llvm.mlir.undef : !llvm.struct<(ptr, i{{.*}})>
// INT64: %[[struct_with_buffer:.*]] = llvm.insertvalue %[[char_array]], %[[empty_struct]][0] : !llvm.struct<(ptr, i{{.*}})>
// INT64: %{{.*}} = llvm.insertvalue %[[c10]], %[[struct_with_buffer]][1] : !llvm.struct<(ptr, i{{.*}})>
// INT64-NEXT: llvm.return
// INT32-LABEL: llvm.func @test_embox
// INT32-SAME: %[[char_array:.*]]: !llvm.ptr)
// INT32: %[[c10:.*]] = llvm.mlir.constant(10 : i64) : i64
// INT32: %[[empty_struct:.*]] = llvm.mlir.undef : !llvm.struct<(ptr, i32)>
// INT32: %[[c10_truncated:.*]] = llvm.trunc %[[c10]] : i64 to i32
// INT32: %[[struct_with_buffer:.*]] = llvm.insertvalue %[[char_array]], %[[empty_struct]][0] : !llvm.struct<(ptr, i32)>
// INT32: %{{.*}} = llvm.insertvalue %[[c10_truncated:.*]], %[[struct_with_buffer]][1] : !llvm.struct<(ptr, i32)>
// INT32-NEXT: llvm.return
// -----
// Test fir.unboxchar
func.func @unboxchar_i8(%arg0 : !fir.boxchar<1>) -> () {
%0:2 = fir.unboxchar %arg0 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1>>, i64)
return
}
// INT64-LABEL: llvm.func @unboxchar_i8
// INT64-SAME: %[[box_char:.*]]: !llvm.struct<(ptr, i64)>
// INT64: %{{.*}} = llvm.extractvalue %[[box_char]][0] : !llvm.struct<(ptr, i64)>
// INT64: %{{.*}} = llvm.extractvalue %[[box_char]][1] : !llvm.struct<(ptr, i64)>
// INT64-NEXT: llvm.return
// INT32-LABEL: llvm.func @unboxchar_i8
// INT32-SAME: %[[box_char:.*]]: !llvm.struct<(ptr, i32)>
// INT32: %{{.*}} = llvm.extractvalue %[[box_char]][0] : !llvm.struct<(ptr, i32)>
// INT32: %[[len_unextended:.*]] = llvm.extractvalue %[[box_char]][1] : !llvm.struct<(ptr, i32)>
// INT32: %{{.*}} = llvm.sext %[[len_unextended]] : i32 to i64
// INT32-NEXT: llvm.return
func.func @unboxchar_i32(%arg0 : !fir.boxchar<4>) -> () {
fir.unboxchar %arg0 : (!fir.boxchar<4>) -> (!fir.ref<!fir.char<4>>, i64)
return
}
// INT64-LABEL: llvm.func @unboxchar_i32
// INT64-SAME: %[[box_char:.*]]: !llvm.struct<(ptr, i64)>
// INT64: %{{.*}} = llvm.extractvalue %[[box_char]][0] : !llvm.struct<(ptr, i64)>
// INT64: %{{.*}} = llvm.extractvalue %[[box_char]][1] : !llvm.struct<(ptr, i64)>
// INT64-NEXT: llvm.return
// INT32-LABEL: llvm.func @unboxchar_i32
// INT32-SAME: %[[box_char:.*]]: !llvm.struct<(ptr, i32)>
// INT32: %{{.*}} = llvm.extractvalue %[[box_char]][0] : !llvm.struct<(ptr, i32)>
// INT32: %[[len_unextended:.*]] = llvm.extractvalue %[[box_char]][1] : !llvm.struct<(ptr, i32)>
// INT32: %{{.*}} = llvm.sext %[[len_unextended]] : i32 to i64
// INT32-NEXT: llvm.return
// -----
// Test fir.boxchar_len
func.func @boxchar_len_i8_i32(%arg0 : !fir.boxchar<1>) -> () {
fir.boxchar_len %arg0 : (!fir.boxchar<1>) -> i32
return
}
// INT64-LABEL: llvm.func @boxchar_len_i8_i32
// INT64-SAME: %[[box_char:.*]]: !llvm.struct<(ptr, i64)>
// INT64: %[[len:.*]] = llvm.extractvalue %[[box_char]][1] : !llvm.struct<(ptr, i64)>
// INT64: %{{.*}} = llvm.trunc %[[len]] : i64 to i32
// INT64-NEXT: llvm.return
// INT32-LABEL: llvm.func @boxchar_len_i8_i32
// INT32-SAME: %[[box_char:.*]]: !llvm.struct<(ptr, i32)>
// INT32: %{{.*}} = llvm.extractvalue %[[box_char]][1] : !llvm.struct<(ptr, i32)>
// INT32-NOT: llvm.trunc
// INT32-NOT: llvm.sext
// INT32-NEXT: llvm.return
func.func @boxchar_len_i8_i64(%arg0 : !fir.boxchar<1>) -> () {
fir.boxchar_len %arg0 : (!fir.boxchar<1>) -> i64
return
}
// INT64-LABEL: llvm.func @boxchar_len_i8_i64
// INT64-SAME: %[[box_char:.*]]: !llvm.struct<(ptr, i64)>
// INT64: %{{.*}} = llvm.extractvalue %[[box_char]][1] : !llvm.struct<(ptr, i64)>
// INT64-NOT: llvm.trunc
// INT64-NOT: llvm.sext
// INT64-NEXT: llvm.return
// INT32-LABEL: llvm.func @boxchar_len_i8_i64
// INT32-SAME: %[[box_char:.*]]: !llvm.struct<(ptr, i32)>
// INT32: %[[len:.*]] = llvm.extractvalue %[[box_char]][1] : !llvm.struct<(ptr, i32)>
// INT32: %{{.*}} = llvm.sext %0 : i32 to i64
// INT32-NEXT: llvm.return
func.func @boxchar_len_i32_i32(%arg0 : !fir.boxchar<4>) -> () {
fir.boxchar_len %arg0 : (!fir.boxchar<4>) -> i32
return
}
// INT64-LABEL: llvm.func @boxchar_len_i32_i32
// INT64-SAME: %[[box_char:.*]]: !llvm.struct<(ptr, i64)>
// INT64: %[[len:.*]] = llvm.extractvalue %[[box_char]][1] : !llvm.struct<(ptr, i64)>
// INT64: %{{.*}} = llvm.trunc %[[len]] : i64 to i32
// INT64-NEXT: llvm.return
// INT32-LABEL: llvm.func @boxchar_len_i32_i32
// INT32-SAME: %[[box_char:.*]]: !llvm.struct<(ptr, i32)>
// INT32: %{{.*}} = llvm.extractvalue %[[box_char]][1] : !llvm.struct<(ptr, i32)>
// INT32-NOT: llvm.trunc
// INT32-NOT: llvm.sext
// INT32-NEXT: llvm.return
func.func @boxchar_len_i32_i64(%arg0 : !fir.boxchar<4>) -> (i64) {
%0 = fir.boxchar_len %arg0 : (!fir.boxchar<4>) -> i64
return %0 : i64
}
// INT64-LABEL: llvm.func @boxchar_len_i32_i64
// INT64-SAME: %[[box_char:.*]]: !llvm.struct<(ptr, i64)>
// INT64: %{{.*}} = llvm.extractvalue %[[box_char]][1] : !llvm.struct<(ptr, i64)>
// INT64-NOT: llvm.trunc
// INT64-NOT: llvm.sext
// INT64-NEXT: llvm.return
// INT32-LABEL: llvm.func @boxchar_len_i32_i64
// INT32-SAME: %[[box_char:.*]]: !llvm.struct<(ptr, i32)>
// INT32: %[[len:.*]] = llvm.extractvalue %[[box_char]][1] : !llvm.struct<(ptr, i32)>
// INT32: %{{.*}} = llvm.sext %0 : i32 to i64
// INT32-NEXT: llvm.return