[flang] Relax fir.rebox verifier with characters

Allow fir.rebox input and output element type to differ for characters
if:
- Any of the character type is dynamic. Fortran allows making pointer
assignments between deferred and constant lengths entities, making this
case useful (if the input length is dynamic and the output length constant,
it is a user requirement that the length matches at runtime. There is no
option to check this at runtime, but it could be added as an option to
fir.rebox codegen later if desired).

- Or, there is a slice in the fir.rebox (the fir.rebox can implement a
substring view, hence the constant output and input lengths).

This is only a verifier constraint change, the fir.rebox codegen is not
impacted and already support those cases.

Add related FIR parsing, error, and codegen tests.

Differential Revision: https://reviews.llvm.org/D121710
This commit is contained in:
Jean Perier 2022-03-16 16:08:05 +01:00
parent ccba163d85
commit 7d52beb275
5 changed files with 257 additions and 2 deletions

View File

@ -2152,6 +2152,18 @@ static unsigned getBoxRank(mlir::Type boxTy) {
return 0;
}
/// Test if \p t1 and \p t2 are compatible character types (if they can
/// represent the same type at runtime).
static bool areCompatibleCharacterTypes(mlir::Type t1, mlir::Type t2) {
auto c1 = t1.dyn_cast<fir::CharacterType>();
auto c2 = t2.dyn_cast<fir::CharacterType>();
if (!c1 || !c2)
return false;
if (c1.hasDynamicLen() || c2.hasDynamicLen())
return true;
return c1.getLen() == c2.getLen();
}
mlir::LogicalResult ReboxOp::verify() {
auto inputBoxTy = getBox().getType();
if (fir::isa_unknown_size_box(inputBoxTy))
@ -2205,12 +2217,21 @@ mlir::LogicalResult ReboxOp::verify() {
return emitOpError("result type and shape operand ranks must match");
}
if (inputEleTy != outEleTy)
if (inputEleTy != outEleTy) {
// TODO: check that outBoxTy is a parent type of inputBoxTy for derived
// types.
if (!inputEleTy.isa<fir::RecordType>())
// Character input and output types with constant length may be different if
// there is a substring in the slice, otherwise, they must match. If any of
// the types is a character with dynamic length, the other type can be any
// character type.
const bool typeCanMismatch =
inputEleTy.isa<fir::RecordType>() ||
(getSlice() && inputEleTy.isa<fir::CharacterType>()) ||
areCompatibleCharacterTypes(inputEleTy, outEleTy);
if (!typeCanMismatch)
return emitOpError(
"op input and output element types must match for intrinsic types");
}
return mlir::success();
}

View File

@ -712,6 +712,23 @@ func @test_rebox(%arg0: !fir.box<!fir.array<?xf32>>) {
return
}
func private @bar_rebox_test_char(!fir.box<!fir.array<?x!fir.char<1,?>>>)
// CHECK-LABEL: @test_rebox_char(
func @test_rebox_char(%arg0: !fir.box<!fir.array<?x!fir.char<1,20>>>) {
%c7_i64 = arith.constant 7 : i64
%c1_i64 = arith.constant 1 : i64
%c0 = arith.constant 0 : index
%c1 = arith.constant 1 : index
%0:3 = fir.box_dims %arg0, %c0 : (!fir.box<!fir.array<?x!fir.char<1,20>>>, index) -> (index, index, index)
%1 = fir.slice %c1, %0#1, %c1_i64 substr %c1_i64, %c7_i64 : (index, index, i64, i64, i64) -> !fir.slice<1>
// CHECK: fir.rebox %{{.*}} [%{{.*}}] : (!fir.box<!fir.array<?x!fir.char<1,20>>>, !fir.slice<1>) -> !fir.box<!fir.array<?x!fir.char<1,?>>>
%2 = fir.rebox %arg0 [%1] : (!fir.box<!fir.array<?x!fir.char<1,20>>>, !fir.slice<1>) -> !fir.box<!fir.array<?x!fir.char<1,?>>>
fir.call @bar_rebox_test_char(%2) : (!fir.box<!fir.array<?x!fir.char<1,?>>>) -> ()
return
}
func private @array_func() -> !fir.array<?x!fir.char<1,?>>
// CHECK-LABEL: @test_save_result(
func @test_save_result(%buffer: !fir.ref<!fir.array<?x!fir.char<1,?>>>) {
%c100 = arith.constant 100 : index

View File

@ -132,6 +132,16 @@ func @bad_rebox_11(%arg0: !fir.box<!fir.array<?x?xf32>>) {
// -----
func @test_rebox_char(%arg0: !fir.box<!fir.array<?x!fir.char<1,20>>>) {
%c10 = arith.constant 10 : index
%1 = fir.shape %c10, %c10 : (index, index) -> !fir.shape<2>
// expected-error@+1{{op input and output element types must match for intrinsic types}}
%2 = fir.rebox %arg0(%1) : (!fir.box<!fir.array<?x!fir.char<1,20>>>, !fir.shape<2>) -> !fir.box<!fir.array<10x10x!fir.char<1,10>>>
return
}
// -----
func @array_access(%arr : !fir.ref<!fir.array<?x?xf32>>) {
%c1 = arith.constant 1 : index
%c100 = arith.constant 100 : index

View File

@ -0,0 +1,70 @@
// Test translation to llvm IR of fir.rebox with substring array sections.
// RUN: tco -o - -cg-rewrite --fir-to-llvm-ir -cse %s | FileCheck %s
// Test a fir.rebox with a substring on a character array with constant
// length (like c(:)(2:*) where c is a fir.box array with constant length).
// CHECK-LABEL: llvm.func @char_section(
// CHECK-SAME: %[[VAL_0:.*]]: !llvm.ptr<[[char20_descriptor_t:.*]]>)>>) {
func @char_section(%arg0: !fir.box<!fir.array<?x!fir.char<1,20>>>) {
%c7_i64 = arith.constant 7 : i64
%c1_i64 = arith.constant 1 : i64
%c0 = arith.constant 0 : index
%c1 = arith.constant 1 : index
%0:3 = fir.box_dims %arg0, %c0 : (!fir.box<!fir.array<?x!fir.char<1,20>>>, index) -> (index, index, index)
%1 = fir.slice %c1, %0#1, %c1_i64 substr %c1_i64, %c7_i64 : (index, index, i64, i64, i64) -> !fir.slice<1>
// Only test the computation of the base address offset computation accounting for the substring
// CHECK: %[[VAL_4:.*]] = llvm.mlir.constant(1 : i64) : i64
// CHECK: %[[VAL_7:.*]] = llvm.mlir.constant(0 : i32) : i32
// CHECK: %[[VAL_30:.*]] = llvm.mlir.constant(0 : i64) : i64
// CHECK: %[[VAL_37:.*]] = llvm.getelementptr %[[VAL_0]]{{\[}}%[[VAL_7]], 0] : (!llvm.ptr<[[char20_descriptor_t]]>)>>, i32) -> !llvm.ptr<ptr<array<20 x i8>>>
// CHECK: %[[VAL_38:.*]] = llvm.load %[[VAL_37]] : !llvm.ptr<ptr<array<20 x i8>>>
// CHECK: %[[VAL_39:.*]] = llvm.bitcast %[[VAL_38]] : !llvm.ptr<array<20 x i8>> to !llvm.ptr<array<20 x i8>>
// CHECK: %[[VAL_40:.*]] = llvm.getelementptr %[[VAL_39]]{{\[}}%[[VAL_30]], %[[VAL_4]]] : (!llvm.ptr<array<20 x i8>>, i64, i64) -> !llvm.ptr<array<20 x i8>>
// CHECK: llvm.bitcast %[[VAL_40]] : !llvm.ptr<array<20 x i8>> to !llvm.ptr<i8>
// More offset computation with descriptor strides and triplets that is not character specific ...
%2 = fir.rebox %arg0 [%1] : (!fir.box<!fir.array<?x!fir.char<1,20>>>, !fir.slice<1>) -> !fir.box<!fir.array<?x!fir.char<1,?>>>
fir.call @bar(%2) : (!fir.box<!fir.array<?x!fir.char<1,?>>>) -> ()
return
}
// Test a rebox of an array section like x(3:60:9)%c(2:8) with both a triplet, a component and a substring where x is a fir.box.
// CHECK-LABEL: llvm.func @foo(
// CHECK-SAME: %[[VAL_0:.*]]: !llvm.ptr<struct<(ptr<[[struct_t:.*]]>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>>) {
func private @bar(!fir.box<!fir.array<?x!fir.char<1,?>>>)
func @foo(%arg0: !fir.box<!fir.array<?x!fir.type<t{i:i32,c:!fir.char<1,10>}>>>) {
%c7_i64 = arith.constant 7 : i64
%c1_i64 = arith.constant 1 : i64
%c9_i64 = arith.constant 9 : i64
%c60_i64 = arith.constant 60 : i64
%c3_i64 = arith.constant 3 : i64
%0 = fir.field_index c, !fir.type<t{i:i32,c:!fir.char<1,10>}>
%1 = fir.slice %c3_i64, %c60_i64, %c9_i64 path %0 substr %c1_i64, %c7_i64 : (i64, i64, i64, !fir.field, i64, i64) -> !fir.slice<1>
// Only test the computation of the base address offset computation accounting for the substring of the component
// CHECK: %[[VAL_1:.*]] = llvm.mlir.constant(1 : i32) : i32
// CHECK: %[[VAL_4:.*]] = llvm.mlir.constant(1 : i64) : i64
// CHECK: %[[VAL_17:.*]] = llvm.mlir.constant(0 : i32) : i32
// CHECK: %[[VAL_21:.*]] = llvm.mlir.constant(0 : i64) : i64
// CHECK: %[[VAL_30:.*]] = llvm.getelementptr %[[VAL_0]]{{\[}}%[[VAL_17]], 0] : (!llvm.ptr<[[struct_t_descriptor:.*]]>, i32) -> !llvm.ptr<ptr<[[struct_t]]>>
// CHECK: %[[VAL_31:.*]] = llvm.load %[[VAL_30]] : !llvm.ptr<ptr<[[struct_t]]>>
// CHECK: %[[VAL_32:.*]] = llvm.bitcast %[[VAL_31]] : !llvm.ptr<[[struct_t]]> to !llvm.ptr<[[struct_t]]>
// CHECK: %[[VAL_33:.*]] = llvm.getelementptr %[[VAL_32]]{{\[}}%[[VAL_21]], 1] : (!llvm.ptr<[[struct_t]]>, i64) -> !llvm.ptr<[[struct_t]]>
// CHECK: %[[VAL_34:.*]] = llvm.getelementptr %[[VAL_33]]{{\[}}%[[VAL_4]]] : (!llvm.ptr<[[struct_t]]>, i64) -> !llvm.ptr<[[struct_t]]>
// CHECK: llvm.bitcast %[[VAL_34]] : !llvm.ptr<[[struct_t]]> to !llvm.ptr<i8>
// More offset computation with descriptor strides and triplets that is not character specific ...
%2 = fir.rebox %arg0 [%1] : (!fir.box<!fir.array<?x!fir.type<t{i:i32,c:!fir.char<1,10>}>>>, !fir.slice<1>) -> !fir.box<!fir.array<?x!fir.char<1,?>>>
fir.call @bar(%2) : (!fir.box<!fir.array<?x!fir.char<1,?>>>) -> ()
return
}

137
flang/test/Fir/rebox.fir Normal file
View File

@ -0,0 +1,137 @@
// RUN: fir-opt %s | tco | FileCheck %s
// Test applying slice on fir.box
// subroutine foo(x)
// real :: x(3:, 4:)
// call bar(x(5, 6:80:3))
// end subroutine
func private @bar1(!fir.box<!fir.array<?xf32>>)
// CHECK-LABEL: define void @test_rebox_1(
// CHECK-SAME: { float*, i64, i32, i8, i8, i8, i8, [2 x [3 x i64]] }* %[[INBOX:.*]])
func @test_rebox_1(%arg0: !fir.box<!fir.array<?x?xf32>>) {
// CHECK: %[[OUTBOX_ALLOC:.*]] = alloca { float*, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }
%c2 = arith.constant 2 : index
%c3 = arith.constant 3 : index
%c4 = arith.constant 4 : index
%c5 = arith.constant 5 : index
%c6 = arith.constant 6 : index
%c80 = arith.constant 80 : index
%undef = fir.undefined index
%0 = fir.slice %c5, %undef, %undef, %c6, %c80, %c3 : (index, index, index, index, index, index) -> !fir.slice<2>
%1 = fir.shift %c3, %c4 : (index, index) -> !fir.shift<2>
// CHECK: %[[INSTRIDE_0_GEP:.*]] = getelementptr { float*, i64, i32, i8, i8, i8, i8, [2 x [3 x i64]] }, { float*, i64, i32, i8, i8, i8, i8, [2 x [3 x i64]] }* %[[INBOX]], i32 0, i32 7, i64 0, i32 2
// CHECK: %[[INSTRIDE_0:.]] = load i64, i64* %[[INSTRIDE_0_GEP]]
// CHECK: %[[INSTRIDE_1_GEP:.*]] = getelementptr { float*, i64, i32, i8, i8, i8, i8, [2 x [3 x i64]] }, { float*, i64, i32, i8, i8, i8, i8, [2 x [3 x i64]] }* %[[INBOX]], i32 0, i32 7, i64 1, i32 2
// CHECK: %[[INSTRIDE_1:.*]] = load i64, i64* %[[INSTRIDE_1_GEP]]
// CHECK: %[[INBASE_GEP:.*]] = getelementptr { float*, i64, i32, i8, i8, i8, i8, [2 x [3 x i64]] }, { float*, i64, i32, i8, i8, i8, i8, [2 x [3 x i64]] }* %[[INBOX]], i32 0, i32 0
// CHECK: %[[INBASE:.*]] = load float*, float** %[[INBASE_GEP]]
// CHECK: %[[VOIDBASE:.*]] = bitcast float* %[[INBASE]] to i8*
// CHECK: %[[OFFSET_0:.*]] = mul i64 2, %[[INSTRIDE_0]]
// CHECK: %[[VOIDBASE0:.*]] = getelementptr i8, i8* %[[VOIDBASE]], i64 %[[OFFSET_0]]
// CHECK: %[[OFFSET_1:.*]] = mul i64 2, %[[INSTRIDE_1]]
// CHECK: %[[VOIDBASE1:.*]] = getelementptr i8, i8* %[[VOIDBASE0]], i64 %[[OFFSET_1]]
// CHECK: %[[OUTSTRIDE0:.*]] = mul i64 3, %[[INSTRIDE_1]]
// CHECK: %[[OUTBOX0:.*]] = insertvalue { float*, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } { float* undef, i64 4, i32 {{.*}}, i8 1, i8 27, i8 0, i8 0, [1 x [3 x i64]] [{{.*}} [i64 1, i64 25, i64 undef]] }, i64 %[[OUTSTRIDE0]], 7, 0, 2
// CHECK: %[[OUTBASE:.*]] = bitcast i8* %[[VOIDBASE1]] to float*
// CHECK: %[[OUTBOX1:.*]] = insertvalue { float*, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %[[OUTBOX0]], float* %[[OUTBASE]], 0
// CHECK: store { float*, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %[[OUTBOX1]], { float*, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }* %[[OUTBOX_ALLOC]], align 8
%2 = fir.rebox %arg0(%1) [%0] : (!fir.box<!fir.array<?x?xf32>>, !fir.shift<2>, !fir.slice<2>) -> !fir.box<!fir.array<?xf32>>
// CHECK: call void @bar1({ float*, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }* %[[OUTBOX_ALLOC]])
fir.call @bar1(%2) : (!fir.box<!fir.array<?xf32>>) -> ()
return
}
// Test that character length is propagated in rebox
// subroutine foo(x)
// character(*) :: x(:, :)
// call bar(x(4:30:1, 4:30:1))
// end subroutine
func private @bar_rebox_test2(!fir.box<!fir.array<?x?x!fir.char<1,?>>>)
// CHECK-LABEL: define void @test_rebox_2(
// CHECK-SAME: { i8*, i64, i32, i8, i8, i8, i8, [2 x [3 x i64]] }* %[[INBOX:.*]])
func @test_rebox_2(%arg0: !fir.box<!fir.array<?x?x!fir.char<1,?>>>) {
%c1 = arith.constant 1 : index
%c4 = arith.constant 4 : index
%c30 = arith.constant 30 : index
%0 = fir.slice %c4, %c30, %c1, %c4, %c30, %c1 : (index, index, index, index, index, index) -> !fir.slice<2>
// CHECK: %[[OUTBOX:.*]] = alloca { i8*, i64, i32, i8, i8, i8, i8, [2 x [3 x i64]] }
// CHECK: %[[LEN_GEP:.*]] = getelementptr { i8*, i64, i32, i8, i8, i8, i8, [2 x [3 x i64]] }, { i8*, i64, i32, i8, i8, i8, i8, [2 x [3 x i64]] }* %[[INBOX]], i32 0, i32 1
// CHECK: %[[LEN:.*]] = load i64, i64* %[[LEN_GEP]]
// CHECK: insertvalue { i8*, i64, i32, i8, i8, i8, i8, [2 x [3 x i64]] } undef, i64 %[[LEN]], 1
%1 = fir.rebox %arg0 [%0] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, !fir.slice<2>) -> !fir.box<!fir.array<?x?x!fir.char<1,?>>>
fir.call @bar_rebox_test2(%1) : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> ()
return
}
// Test setting a new shape on a fir.box
// subroutine foo(x)
// real :: x(:)
// real, pointer(:, :, :), p
// p(2:5, 3:7, 4:9) => x
// call bar(p)
// end subroutine
func private @bar_rebox_test3(!fir.box<!fir.array<?x?x?xf32>>)
// CHECK-LABEL: define void @test_rebox_3(
// CHECK-SAME: { float*, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }* %[[INBOX:.*]])
func @test_rebox_3(%arg0: !fir.box<!fir.array<?xf32>>) {
// CHECK: %[[OUTBOX_ALLOC:.*]] = alloca { float*, i64, i32, i8, i8, i8, i8, [3 x [3 x i64]] }
%c2 = arith.constant 2 : index
%c3 = arith.constant 3 : index
%c4 = arith.constant 4 : index
%c5 = arith.constant 5 : index
%1 = fir.shape_shift %c2, %c3, %c3, %c4, %c4, %c5 : (index, index, index, index, index, index) -> !fir.shapeshift<3>
// CHECK: %[[INSTRIDE_GEP:.*]] = getelementptr { float*, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, { float*, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }* %[[INBOX]], i32 0, i32 7, i64 0, i32 2
// CHECK: %[[INSTRIDE:.*]] = load i64, i64* %[[INSTRIDE_GEP]]
// CHECK: %[[INBASE_GEP:.*]] = getelementptr { float*, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, { float*, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }* %[[INBOX]], i32 0, i32 0
// CHECK: %[[INBASE:.*]] = load float*, float** %[[INBASE_GEP]]
// CHECK: %[[VOIDBASE:.*]] = bitcast float* %[[INBASE]] to i8*
// CHECK: %[[OUTSTRIDE1:.*]] = mul i64 3, %[[INSTRIDE]]
// CHECK: %[[OUTSTRIDE2:.*]] = mul i64 4, %[[OUTSTRIDE1]]
// CHECK: %[[OUTBOX0:.*]] = insertvalue { float*, i64, i32, i8, i8, i8, i8, [3 x [3 x i64]] } { float* undef, i64 4, i32 {{.*}}, i8 3, i8 27, i8 0, i8 0, [3 x [3 x i64]] [{{.*}} [i64 2, i64 3, i64 undef], [3 x i64] undef, [3 x i64] undef] }, i64 %[[INSTRIDE]], 7, 0, 2
// CHECK: %[[OUTBOX1:.*]] = insertvalue { float*, i64, i32, i8, i8, i8, i8, [3 x [3 x i64]] } %[[OUTBOX0]], i64 3, 7, 1, 0
// CHECK: %[[OUTBOX2:.*]] = insertvalue { float*, i64, i32, i8, i8, i8, i8, [3 x [3 x i64]] } %[[OUTBOX1]], i64 4, 7, 1, 1
// CHECK: %[[OUTBOX3:.*]] = insertvalue { float*, i64, i32, i8, i8, i8, i8, [3 x [3 x i64]] } %[[OUTBOX2]], i64 %[[OUTSTRIDE1]], 7, 1, 2
// CHECK: %[[OUTBOX4:.*]] = insertvalue { float*, i64, i32, i8, i8, i8, i8, [3 x [3 x i64]] } %[[OUTBOX3]], i64 4, 7, 2, 0
// CHECK: %[[OUTBOX5:.*]] = insertvalue { float*, i64, i32, i8, i8, i8, i8, [3 x [3 x i64]] } %[[OUTBOX4]], i64 5, 7, 2, 1
// CHECK: %[[OUTBOX6:.*]] = insertvalue { float*, i64, i32, i8, i8, i8, i8, [3 x [3 x i64]] } %[[OUTBOX5]], i64 %[[OUTSTRIDE2]], 7, 2, 2
// CHECK: %[[OUTBASE:.*]] = bitcast i8* %[[VOIDBASE]] to float*
// CHECK: %[[OUTBOX7:.*]] = insertvalue { float*, i64, i32, i8, i8, i8, i8, [3 x [3 x i64]] } %[[OUTBOX6]], float* %[[OUTBASE]], 0
// CHECK: store { float*, i64, i32, i8, i8, i8, i8, [3 x [3 x i64]] } %[[OUTBOX7]], { float*, i64, i32, i8, i8, i8, i8, [3 x [3 x i64]] }* %[[OUTBOX_ALLOC]]
%2 = fir.rebox %arg0(%1) : (!fir.box<!fir.array<?xf32>>, !fir.shapeshift<3>) -> !fir.box<!fir.array<?x?x?xf32>>
// CHECK: call void @bar_rebox_test3({ float*, i64, i32, i8, i8, i8, i8, [3 x [3 x i64]] }* %[[OUTBOX_ALLOC]])
fir.call @bar_rebox_test3(%2) : (!fir.box<!fir.array<?x?x?xf32>>) -> ()
return
}
// Test reboxing of character entities where the input has dynamic length and the output has compile
// time constant length.
// CHECK-LABEL: define void @test_rebox_4(
// CHECK-SAME: { i8*, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }* %[[INPUT:.*]])
func @test_rebox_4(%arg0: !fir.box<!fir.array<?x!fir.char<1,?>>>) {
// CHECK: %[[NEWBOX_STORAGE:.*]] = alloca { [10 x i8]*, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }
// CHECK: %[[EXTENT_GEP:.*]] = getelementptr {{{.*}}}, { i8*, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }* %[[INPUT]], i32 0, i32 7, i64 0, i32 1
// CHECK: %[[EXTENT:.*]] = load i64, i64* %[[EXTENT_GEP]]
// CHECK: %[[STRIDE_GEP:.*]] = getelementptr { i8*, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, { i8*, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }* %[[INPUT]], i32 0, i32 7, i64 0, i32 2
// CHECK: %[[STRIDE:.*]] = load i64, i64* %[[STRIDE_GEP]]
// CHECK: %[[BASE_GEP:.*]] = getelementptr { i8*, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, { i8*, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }* %[[INPUT]], i32 0, i32 0
// CHECK: %[[BASE:.*]] = load i8*, i8** %[[BASE_GEP]]
// CHECK: %[[NEWBOX1:.*]] = insertvalue {{{.*}}} { [10 x i8]* undef, i64 10, i32 20180515, i8 1, i8 40, i8 1, i8 0, [1 x [3 x i64]] [{{.*}} [i64 1, i64 undef, i64 undef]] }, i64 %[[EXTENT]], 7, 0, 1
// CHECK: %[[NEWBOX2:.*]] = insertvalue { [10 x i8]*, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %[[NEWBOX1]], i64 %[[STRIDE]], 7, 0, 2
// CHECK: %[[BASE_CAST:.*]] = bitcast i8* %12 to [10 x i8]*
// CHECK: %[[NEWBOX3:.*]] = insertvalue { [10 x i8]*, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %[[NEWBOX2]], [10 x i8]* %[[BASE_CAST]], 0
// CHECK: store { [10 x i8]*, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %[[NEWBOX3]], { [10 x i8]*, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }* %[[NEWBOX_STORAGE]]
// CHECK: call void @bar_test_rebox_4({ [10 x i8]*, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }* %[[NEWBOX_STORAGE]])
%1 = fir.rebox %arg0 : (!fir.box<!fir.array<?x!fir.char<1,?>>>) -> !fir.box<!fir.ptr<!fir.array<?x!fir.char<1,10>>>>
fir.call @bar_test_rebox_4(%1) : (!fir.box<!fir.ptr<!fir.array<?x!fir.char<1,10>>>>) -> ()
return
}
func private @bar_test_rebox_4(!fir.box<!fir.ptr<!fir.array<?x!fir.char<1,10>>>>)