diff --git a/flang/lib/Optimizer/CodeGen/Target.cpp b/flang/lib/Optimizer/CodeGen/Target.cpp index 47e6b0d57ab27..9ec2f04489aee 100644 --- a/flang/lib/Optimizer/CodeGen/Target.cpp +++ b/flang/lib/Optimizer/CodeGen/Target.cpp @@ -138,6 +138,13 @@ struct TargetX86_64 : public GenericTarget { // two distinct double arguments marshal.emplace_back(eleTy, AT{}); marshal.emplace_back(eleTy, AT{}); + } else if (sem == &llvm::APFloat::IEEEquad()) { + // Use a type that will be translated into LLVM as: + // { fp128, fp128 } struct of 2 fp128, byval, align 16 + mlir::TypeRange range = {eleTy, eleTy}; + marshal.emplace_back(fir::ReferenceType::get( + mlir::TupleType::get(eleTy.getContext(), range)), + AT{/*align=*/16, /*byval=*/true}); } else { TODO(loc, "complex for this precision"); } @@ -157,6 +164,13 @@ struct TargetX86_64 : public GenericTarget { mlir::TypeRange range = {eleTy, eleTy}; marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), AT{}); + } else if (sem == &llvm::APFloat::IEEEquad()) { + // Use a type that will be translated into LLVM as: + // { fp128, fp128 } struct of 2 fp128, sret, align 16 + mlir::TypeRange range = {eleTy, eleTy}; + marshal.emplace_back(fir::ReferenceType::get( + mlir::TupleType::get(eleTy.getContext(), range)), + AT{/*align=*/16, /*byval=*/false, /*sret=*/true}); } else { TODO(loc, "complex for this precision"); } diff --git a/flang/lib/Optimizer/CodeGen/TargetRewrite.cpp b/flang/lib/Optimizer/CodeGen/TargetRewrite.cpp index c88d4317d29a9..f1e58d42779ea 100644 --- a/flang/lib/Optimizer/CodeGen/TargetRewrite.cpp +++ b/flang/lib/Optimizer/CodeGen/TargetRewrite.cpp @@ -744,14 +744,33 @@ class TargetRewrite : public fir::TargetRewriteBase { auto argTy = std::get(tup); if (attr.isSRet()) { unsigned argNo = newInTys.size(); - fixups.emplace_back( - FixupTy::Codes::ReturnAsStore, argNo, [=](mlir::func::FuncOp func) { - func.setArgAttr(argNo, "llvm.sret", rewriter->getUnitAttr()); - }); + if (auto align = attr.getAlignment()) + fixups.emplace_back( + FixupTy::Codes::ReturnAsStore, argNo, [=](mlir::func::FuncOp func) { + func.setArgAttr(argNo, "llvm.sret", rewriter->getUnitAttr()); + func.setArgAttr(argNo, "llvm.align", + rewriter->getIntegerAttr( + rewriter->getIntegerType(32), align)); + }); + else + fixups.emplace_back( + FixupTy::Codes::ReturnAsStore, argNo, [=](mlir::func::FuncOp func) { + func.setArgAttr(argNo, "llvm.sret", rewriter->getUnitAttr()); + }); newInTys.push_back(argTy); return; + } else { + if (auto align = attr.getAlignment()) + fixups.emplace_back(FixupTy::Codes::ReturnType, newResTys.size(), + [=](mlir::func::FuncOp func) { + func.setArgAttr( + newResTys.size(), "llvm.align", + rewriter->getIntegerAttr( + rewriter->getIntegerType(32), align)); + }); + else + fixups.emplace_back(FixupTy::Codes::ReturnType, newResTys.size()); } - fixups.emplace_back(FixupTy::Codes::ReturnType, newResTys.size()); newResTys.push_back(argTy); } diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp index 069fa4344c5d1..6443d5c6eca42 100644 --- a/flang/lib/Optimizer/Dialect/FIROps.cpp +++ b/flang/lib/Optimizer/Dialect/FIROps.cpp @@ -1561,7 +1561,7 @@ struct UndoComplexPattern : public mlir::RewritePattern { return mlir::failure(); auto insval2 = mlir::dyn_cast_or_null( insval.getAdt().getDefiningOp()); - if (!insval2 || !mlir::isa(insval2.getAdt().getDefiningOp())) + if (!insval2) return mlir::failure(); auto binf = mlir::dyn_cast_or_null(insval.getVal().getDefiningOp()); auto binf2 = diff --git a/flang/test/Fir/fir-int-conversion.fir b/flang/test/Fir/fir-int-conversion.fir new file mode 100644 index 0000000000000..700272bf13220 --- /dev/null +++ b/flang/test/Fir/fir-int-conversion.fir @@ -0,0 +1,35 @@ +// RUN: fir-opt --split-input-file --fir-to-llvm-ir %s | FileCheck --check-prefixes=COMMON,DEFAULT %s +// RUN: fir-opt --kind-mapping="i1:4,i2:8,i4:16,i8:32,i16:64" --split-input-file --fir-to-llvm-ir %s | FileCheck --check-prefixes=COMMON,ALL-CUSTOM %s +// RUN: fir-opt --kind-mapping="i2:1,i4:8,i16:32" --split-input-file --fir-to-llvm-ir %s | FileCheck --check-prefixes=COMMON,SOME-CUSTOM %s + +// Test `!fir.integer` conversion with and without kind-mapping string + +func.func private @foo0(%arg0: !fir.int<1>) +// COMMON-LABEL: foo0 +// DEFAULT-SAME: i8 +// ALL-CUSTOM-SAME: i4 +// SOME-CUSTOM-SAME: i8 + +func.func private @foo1(%arg0: !fir.int<2>) +// COMMON-LABEL: foo1 +// DEFAULT-SAME: i16 +// ALL-CUSTOM-SAME: i8 +// SOME-CUSTOM-SAME: i1 + +func.func private @foo2(%arg0: !fir.int<4>) +// COMMON-LABEL: foo2 +// DEFAULT-SAME: i32 +// ALL-CUSTOM-SAME: i16 +// SOME-CUSTOM-SAME: i8 + +func.func private @foo3(%arg0: !fir.int<8>) +// COMMON-LABEL: foo3 +// DEFAULT-SAME: i64 +// ALL-CUSTOM-SAME: i32 +// SOME-CUSTOM-SAME: i64 + +func.func private @foo4(%arg0: !fir.int<16>) +// COMMON-LABEL: foo4 +// DEFAULT-SAME: i128 +// ALL-CUSTOM-SAME: i64 +// SOME-CUSTOM-SAME: i32 diff --git a/flang/test/Fir/loop10.fir b/flang/test/Fir/loop10.fir new file mode 100644 index 0000000000000..e1c908bb10671 --- /dev/null +++ b/flang/test/Fir/loop10.fir @@ -0,0 +1,25 @@ +// RUN: tco %s | FileCheck %s + +// CHECK: @x({{.*}} %[[ADDR:.*]]) +func.func @x(%addr : !fir.ref>) -> index { + %c0 = arith.constant 0 : index + %c10 = arith.constant 10 : index + %c1 = arith.constant 1 : index + // CHECK-DAG: %[[R:.*]] = phi i64 {{.*}} [ 0, + // CHECK-DAG: %[[ROW:.*]] = phi i64 {{.*}} [ 11, + // CHECK: icmp sgt i64 %[[ROW]], 0 + fir.do_loop %iv = %c0 to %c10 step %c1 { + // CHECK-DAG: %[[C:.*]] = phi i64 {{.*}} [ 0, + // CHECK-DAG: %[[COL:.*]] = phi i64 {{.*}} [ 11, + // CHECK: icmp sgt i64 %[[COL]], 0 + fir.do_loop %jv = %c0 to %c10 step %c1 { + // CHECK: getelementptr {{.*}} %[[ADDR]], i64 0, i64 %[[R]], i64 %[[C]] + %ptr = fir.coordinate_of %addr, %jv, %iv : (!fir.ref>, index, index) -> !fir.ref + %c22 = arith.constant 22 : i32 + // CHECK: store i32 22, + fir.store %c22 to %ptr : !fir.ref + } + } + // CHECK: ret i64 10 + return %c10 : index +} diff --git a/flang/test/Fir/pdt.fir b/flang/test/Fir/pdt.fir new file mode 100644 index 0000000000000..4464b897414a1 --- /dev/null +++ b/flang/test/Fir/pdt.fir @@ -0,0 +1,110 @@ +// RUN: tco %s | FileCheck %s + +// CHECK-LINE: define i64 @_QTtP.mem.size(i32 %0, i16 %1) +func.func @_QTtP.mem.size(%0 : i32, %1 : i16) -> index { + %2 = call @_QTtP.f1.size(%0, %1) : (i32, i16) -> index + %3 = call @_QTtP.f2.size(%0, %1) : (i32, i16) -> index + %4 = arith.addi %2, %3 : index + // CHECK: ret i64 8 + return %4 : index +} +// CHECK-LINE: define i64 @_QTtP.f1.size(i32 %0, i16 %1) +func.func @_QTtP.f1.size(%0 : i32, %1 : i16) -> index { + %2 = arith.constant 4 : index + // CHECK: ret i64 4 + return %2 : index +} +// CHECK-LINE: define i64 @_QTtP.f2.size(i32 %0, i16 %1) +func.func @_QTtP.f2.size(%0 : i32, %1 : i16) -> index { + %2 = arith.constant 4 : index + // CHECK: ret i64 4 + return %2 : index +} +// CHECK-LINE: define i32 @_QTtP.f1.offset(i32 %0, i16 %1) +func.func @_QTtP.f1.offset(%0 : i32, %1 : i16) -> i32 { + %2 = arith.constant 0 : i32 + // CHECK: ret i32 0 + return %2 : i32 +} +// CHECK-LINE: define i32 @_QTtP.f2.offset(i32 %0, i16 %1) +func.func @_QTtP.f2.offset(%0 : i32, %1 : i16) -> i32 { + %2 = arith.constant 4 : i32 + // CHECK: ret i32 4 + return %2 : i32 +} + +// program p +// type t(p1,p2) +// integer, len :: p1 +// integer(kind=2), len :: p2 +// integer f1 +// real f2 +// end type t +// type(t) var +// var%f1 = 4 +// end program p + +// CHECK-LINE: define void @_QQmain(i32 %0, i16 %1) +func.func @_QQmain(%arg0 : i32, %arg1 : i16) { + // CHECK: %[[size:.*]] = call i64 @_QTtP.mem.size(i32 %0, i16 %1) + // CHECK: %[[alloc:.*]] = alloca i8, i64 %[[size]] + %0 = fir.alloca !fir.type<_QTt(p1:i32,p2:i16){f1:i32,f2:f32}>(%arg0, %arg1 : i32, i16) {name = "_QEvar"} + %1 = fir.field_index f1, !fir.type<_QTt(p1:i32,p2:i16){f1:i32,f2:f32}>(%arg0, %arg1 : i32, i16) + %2 = fir.coordinate_of %0, %1 : (!fir.ref>, !fir.field) -> !fir.ref + %c4_i32 = arith.constant 4 : i32 + fir.store %c4_i32 to %2 : !fir.ref + return +} + +// CHECK-LINE: define i64 @_QTt1P.mem.size(i32 %0, i32 %1) +func.func @_QTt1P.mem.size(%0 : i32, %1 : i32) -> index { + // CHECK: call i64 @_QTt1P.f1.size + %2 = call @_QTt1P.f1.size(%0, %1) : (i32, i32) -> index + // CHECK: call i64 @_QTt1P.f2.size + %3 = call @_QTt1P.f2.size(%0, %1) : (i32, i32) -> index + %4 = arith.addi %2, %3 : index + return %4 : index +} +// CHECK-LINE: define i64 @_QTt1P.f1.size(i32 %0, i32 %1) +func.func @_QTt1P.f1.size(%0 : i32, %1 : i32) -> index { + %2 = fir.convert %0 : (i32) -> index + return %2 : index +} +// CHECK-LINE: define i64 @_QTt1P.f2.size(i32 %0, i32 %1) +func.func @_QTt1P.f2.size(%0 : i32, %1 : i32) -> index { + %2 = fir.convert %1 : (i32) -> index + return %2 : index +} +// CHECK-LINE: define i32 @_QTt1P.f1.offset(i32 %0, i32 %1) +func.func @_QTt1P.f1.offset(%0 : i32, %1 : i32) -> i32 { + %2 = arith.constant 0 : i32 + return %2 : i32 +} +// CHECK-LINE: define i32 @_QTt1P.f2.offset(i32 %0, i32 %1) +func.func @_QTt1P.f2.offset(%0 : i32, %1 : i32) -> i32 { + return %0 : i32 +} +// subroutine foo(i,j) +// type t(p1,p2) +// integer, len :: p1 +// integer, len :: p2 +// character(LEN=p1) :: f1 +// character(LEN=p2) :: f2 +// end type t +// type(t(i,j)) var +// call bar(var%f2) +// end program p + +func.func private @bar(!fir.ref>) + +// CHECK-LINE: define i8* @_QPfoo(i32 %0, i32 %1) +func.func @_QPfoo(%arg0 : i32, %arg1 : i32) { + // CHECK: %[[size:.*]] = call i64 @_QTt1P.mem.size(i32 %0, i32 %1) + // CHECK: %[[alloc:.*]] = alloca i8, i64 %[[size]] + %0 = fir.alloca !fir.type<_QTt1(p1:i32,p2:i32){f1:!fir.char<1,?>,f2:!fir.char<1,?>}>(%arg0, %arg1 : i32, i32) + %1 = fir.field_index f2, !fir.type<_QTt1>(%arg0, %arg1 : i32, i32) + //%2 = fir.coordinate_of %0, %1 : (!fir.ref>, !fir.field) -> !fir.ref> + %2 = fir.zero_bits !fir.ref> + fir.call @bar(%2) : (!fir.ref>) -> () + return +} diff --git a/flang/test/Fir/real.fir b/flang/test/Fir/real.fir new file mode 100644 index 0000000000000..3487d84f217e8 --- /dev/null +++ b/flang/test/Fir/real.fir @@ -0,0 +1,51 @@ +// Test lowering of REAL operations from FIR to LLVM IR + +// RUN: tco %s | FileCheck %s + +// CHECK-LABEL: @bar +func.func @bar(%a : f16, %b : f32, %c : f64, %d : f80, %e : f128) -> f80 { + // CHECK: fpext half %{{.*}} to x86_fp80 + %1 = fir.convert %a : (f16) -> f80 + // CHECK: fpext float %{{.*}} to x86_fp80 + %2 = fir.convert %b : (f32) -> f80 + // CHECK: fpext double %{{.*}} to x86_fp80 + %3 = fir.convert %c : (f64) -> f80 + // CHECK-NOT: fpext + // CHECK-NOT: fptrunc + %4 = fir.convert %d : (f80) -> f80 + // CHECK: fptrunc fp128 %{{.*}} to x86_fp80 + %5 = fir.convert %e : (f128) -> f80 + // CHECK-NEXT: call x86_fp80 + %6 = fir.call @foop(%1, %2, %3, %4, %5) : (f80, f80, f80, f80, f80) -> f80 + return %6 : f80 +} + +// CHECK-LABEL: @foo +func.func @foo(%a : f128, %b : f128, %c : f128, %d : f128, %e : f128) -> f128 { + // CHECK: fadd fp128 + %1 = arith.addf %a, %b : f128 + // CHECK: fmul fp128 + %2 = arith.mulf %1, %c : f128 + // CHECK: fsub fp128 + %3 = arith.subf %2, %d : f128 + // CHECK: fdiv fp128 + %4 = arith.divf %3, %e : f128 + // CHECK: frem fp128 + %5 = arith.remf %4, %a : f128 + return %5 : f128 +} + +// CHECK-LABEL: @foop +func.func @foop(%a : f80, %b : f80, %c : f80, %d : f80, %e : f80) -> f80 { + // CHECK: fadd x86_fp80 + %1 = arith.addf %a, %b : f80 + // CHECK: fmul x86_fp80 + %2 = arith.mulf %1, %c : f80 + // CHECK: fsub x86_fp80 + %3 = arith.subf %2, %d : f80 + // CHECK: fdiv x86_fp80 + %4 = arith.divf %3, %e : f80 + // CHECK: frem x86_fp80 + %5 = arith.remf %4, %a : f80 + return %5 : f80 +} diff --git a/flang/test/Fir/recursive-type-tco.fir b/flang/test/Fir/recursive-type-tco.fir new file mode 100644 index 0000000000000..9933f727af12b --- /dev/null +++ b/flang/test/Fir/recursive-type-tco.fir @@ -0,0 +1,11 @@ +// Test lowering FIR to LLVM IR for a recursive type + +// RUN: tco %s | FileCheck %s + +// CHECK-LABEL: %t = type { ptr } +!t = !fir.type>}> + +// CHECK-LABEL: @a(%t %{{.*}}) +func.func @a(%a : !t) { + return +} diff --git a/flang/test/Fir/target-rewrite-complex.fir b/flang/test/Fir/target-rewrite-complex.fir index 515c18a10be8c..b83d582178140 100644 --- a/flang/test/Fir/target-rewrite-complex.fir +++ b/flang/test/Fir/target-rewrite-complex.fir @@ -53,7 +53,7 @@ func.func @returncomplex4() -> !fir.complex<4> { // Test that we rewrite the signature and body of a function that returns a // complex<8>. // I32-LABEL:func @returncomplex8 -// I32-SAME: ([[ARG0:%[0-9A-Za-z]+]]: !fir.ref, !fir.real<8>>> {llvm.sret}) +// I32-SAME: ([[ARG0:%[0-9A-Za-z]+]]: !fir.ref, !fir.real<8>>> {llvm.align = 4 : i32, llvm.sret}) // X64-LABEL: func @returncomplex8() -> tuple, !fir.real<8>> // AARCH64-LABEL: func @returncomplex8() -> tuple, !fir.real<8>> // PPC-LABEL: func @returncomplex8() -> tuple, !fir.real<8>> diff --git a/flang/test/Fir/target-rewrite-complex16.fir b/flang/test/Fir/target-rewrite-complex16.fir new file mode 100644 index 0000000000000..9c4f0af7dd77b --- /dev/null +++ b/flang/test/Fir/target-rewrite-complex16.fir @@ -0,0 +1,125 @@ +// RUN: fir-opt --target-rewrite="target=x86_64-unknown-linux-gnu" %s | FileCheck %s +// UNSUPPORTED: darwin + +// Test that we rewrite the signature and body of a func.function that returns a +// complex<16>. +func.func @returncomplex16() -> !fir.complex<16> { + %1 = fir.undefined !fir.complex<16> + %2 = arith.constant 2.0 : f128 + %3 = fir.convert %2 : (f128) -> !fir.real<16> + %c0 = arith.constant 0 : i32 + %4 = fir.insert_value %1, %3, [0 : i32] : (!fir.complex<16>, !fir.real<16>) -> !fir.complex<16> + %c1 = arith.constant 1 : i32 + %5 = arith.constant -42.0 : f128 + %6 = fir.insert_value %4, %5, [1 : i32] : (!fir.complex<16>, f128) -> !fir.complex<16> + return %6 : !fir.complex<16> +} + +// Test that we rewrite the signature of a func.function that accepts a complex<16>. +func.func private @paramcomplex16(!fir.complex<16>) -> () + +// Test that we rewrite calls to func.functions that return or accept complex<16>. +func.func @callcomplex16() { + %1 = fir.call @returncomplex16() : () -> !fir.complex<16> + fir.call @paramcomplex16(%1) : (!fir.complex<16>) -> () + return +} + +// Test multiple complex<16> parameters and arguments +func.func private @calleemultipleparamscomplex16(!fir.complex<16>, !fir.complex<16>, !fir.complex<16>) -> () + +func.func @multipleparamscomplex16(%z1 : !fir.complex<16>, %z2 : !fir.complex<16>, %z3 : !fir.complex<16>) { + fir.call @calleemultipleparamscomplex16(%z1, %z2, %z3) : (!fir.complex<16>, !fir.complex<16>, !fir.complex<16>) -> () + return +} + +// Test that we rewrite the signature of and calls to a func.function that accepts +// and returns MLIR complex. +func.func private @mlircomplexf128(%z1: complex, %z2: complex) -> complex { + %0 = fir.call @mlircomplexf128(%z1, %z2) : (complex, complex) -> complex + return %0 : complex +} + +// Test that we rewrite the fir.address_of operator. +func.func @addrof() { + %r = fir.address_of(@returncomplex16) : () -> !fir.complex<16> + %p = fir.address_of(@paramcomplex16) : (!fir.complex<16>) -> () + return +} + +// CHECK-LABEL: func.func @returncomplex16( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref, !fir.real<16>>> {llvm.align = 16 : i32, llvm.sret}) { +// CHECK: %[[VAL_1:.*]] = fir.undefined !fir.complex<16> +// CHECK: %[[VAL_2:.*]] = arith.constant 2.000000e+00 : f128 +// CHECK: %[[VAL_3:.*]] = fir.convert %[[VAL_2]] : (f128) -> !fir.real<16> +// CHECK: %[[VAL_4:.*]] = arith.constant 0 : i32 +// CHECK: %[[VAL_5:.*]] = fir.insert_value %[[VAL_1]], %[[VAL_3]], [0 : i32] : (!fir.complex<16>, !fir.real<16>) -> !fir.complex<16> +// CHECK: %[[VAL_6:.*]] = arith.constant 1 : i32 +// CHECK: %[[VAL_7:.*]] = arith.constant -4.200000e+01 : f128 +// CHECK: %[[VAL_8:.*]] = fir.insert_value %[[VAL_5]], %[[VAL_7]], [1 : i32] : (!fir.complex<16>, f128) -> !fir.complex<16> +// CHECK: %[[VAL_9:.*]] = fir.convert %[[VAL_0]] : (!fir.ref, !fir.real<16>>>) -> !fir.ref> +// CHECK: fir.store %[[VAL_8]] to %[[VAL_9]] : !fir.ref> +// CHECK: return +// CHECK: } +// CHECK: func.func private @paramcomplex16(!fir.ref, !fir.real<16>>> {llvm.align = 16 : i32, llvm.byval}) + +// CHECK-LABEL: func.func @callcomplex16() { +// CHECK: %[[VAL_0:.*]] = fir.alloca tuple, !fir.real<16>> +// CHECK: fir.call @returncomplex16(%[[VAL_0]]) : (!fir.ref, !fir.real<16>>>) -> () +// CHECK: %[[VAL_1:.*]] = fir.convert %[[VAL_0]] : (!fir.ref, !fir.real<16>>>) -> !fir.ref> +// CHECK: %[[VAL_2:.*]] = fir.load %[[VAL_1]] : !fir.ref> +// CHECK: %[[VAL_3:.*]] = fir.alloca !fir.complex<16> +// CHECK: fir.store %[[VAL_2]] to %[[VAL_3]] : !fir.ref> +// CHECK: %[[VAL_4:.*]] = fir.convert %[[VAL_3]] : (!fir.ref>) -> !fir.ref, !fir.real<16>>> +// CHECK: fir.call @paramcomplex16(%[[VAL_4]]) : (!fir.ref, !fir.real<16>>>) -> () +// CHECK: return +// CHECK: } +// CHECK: func.func private @calleemultipleparamscomplex16(!fir.ref, !fir.real<16>>> {llvm.align = 16 : i32, llvm.byval}, !fir.ref, !fir.real<16>>> {llvm.align = 16 : i32, llvm.byval}, !fir.ref, !fir.real<16>>> {llvm.align = 16 : i32, llvm.byval}) + +// CHECK-LABEL: func.func @multipleparamscomplex16( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref, !fir.real<16>>> {llvm.align = 16 : i32, llvm.byval}, %[[VAL_1:.*]]: !fir.ref, !fir.real<16>>> {llvm.align = 16 : i32, llvm.byval}, %[[VAL_2:.*]]: !fir.ref, !fir.real<16>>> {llvm.align = 16 : i32, llvm.byval}) { +// CHECK: %[[VAL_3:.*]] = fir.convert %[[VAL_2]] : (!fir.ref, !fir.real<16>>>) -> !fir.ref> +// CHECK: %[[VAL_4:.*]] = fir.load %[[VAL_3]] : !fir.ref> +// CHECK: %[[VAL_5:.*]] = fir.convert %[[VAL_1]] : (!fir.ref, !fir.real<16>>>) -> !fir.ref> +// CHECK: %[[VAL_6:.*]] = fir.load %[[VAL_5]] : !fir.ref> +// CHECK: %[[VAL_7:.*]] = fir.convert %[[VAL_0]] : (!fir.ref, !fir.real<16>>>) -> !fir.ref> +// CHECK: %[[VAL_8:.*]] = fir.load %[[VAL_7]] : !fir.ref> +// CHECK: %[[VAL_9:.*]] = fir.alloca !fir.complex<16> +// CHECK: fir.store %[[VAL_8]] to %[[VAL_9]] : !fir.ref> +// CHECK: %[[VAL_10:.*]] = fir.convert %[[VAL_9]] : (!fir.ref>) -> !fir.ref, !fir.real<16>>> +// CHECK: %[[VAL_11:.*]] = fir.alloca !fir.complex<16> +// CHECK: fir.store %[[VAL_6]] to %[[VAL_11]] : !fir.ref> +// CHECK: %[[VAL_12:.*]] = fir.convert %[[VAL_11]] : (!fir.ref>) -> !fir.ref, !fir.real<16>>> +// CHECK: %[[VAL_13:.*]] = fir.alloca !fir.complex<16> +// CHECK: fir.store %[[VAL_4]] to %[[VAL_13]] : !fir.ref> +// CHECK: %[[VAL_14:.*]] = fir.convert %[[VAL_13]] : (!fir.ref>) -> !fir.ref, !fir.real<16>>> +// CHECK: fir.call @calleemultipleparamscomplex16(%[[VAL_10]], %[[VAL_12]], %[[VAL_14]]) : (!fir.ref, !fir.real<16>>>, !fir.ref, !fir.real<16>>>, !fir.ref, !fir.real<16>>>) -> () +// CHECK: return +// CHECK: } + +// CHECK-LABEL: func.func private @mlircomplexf128( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref> {llvm.align = 16 : i32, llvm.sret}, %[[VAL_1:.*]]: !fir.ref> {llvm.align = 16 : i32, llvm.byval}, %[[VAL_2:.*]]: !fir.ref> {llvm.align = 16 : i32, llvm.byval}) { +// CHECK: %[[VAL_3:.*]] = fir.convert %[[VAL_2]] : (!fir.ref>) -> !fir.ref> +// CHECK: %[[VAL_4:.*]] = fir.load %[[VAL_3]] : !fir.ref> +// CHECK: %[[VAL_5:.*]] = fir.convert %[[VAL_1]] : (!fir.ref>) -> !fir.ref> +// CHECK: %[[VAL_6:.*]] = fir.load %[[VAL_5]] : !fir.ref> +// CHECK: %[[VAL_7:.*]] = fir.alloca tuple +// CHECK: %[[VAL_8:.*]] = fir.alloca complex +// CHECK: fir.store %[[VAL_6]] to %[[VAL_8]] : !fir.ref> +// CHECK: %[[VAL_9:.*]] = fir.convert %[[VAL_8]] : (!fir.ref>) -> !fir.ref> +// CHECK: %[[VAL_10:.*]] = fir.alloca complex +// CHECK: fir.store %[[VAL_4]] to %[[VAL_10]] : !fir.ref> +// CHECK: %[[VAL_11:.*]] = fir.convert %[[VAL_10]] : (!fir.ref>) -> !fir.ref> +// CHECK: fir.call @mlircomplexf128(%[[VAL_7]], %[[VAL_9]], %[[VAL_11]]) : (!fir.ref>, !fir.ref>, !fir.ref>) -> () +// CHECK: %[[VAL_12:.*]] = fir.convert %[[VAL_7]] : (!fir.ref>) -> !fir.ref> +// CHECK: %[[VAL_13:.*]] = fir.load %[[VAL_12]] : !fir.ref> +// CHECK: %[[VAL_14:.*]] = fir.convert %[[VAL_0]] : (!fir.ref>) -> !fir.ref> +// CHECK: fir.store %[[VAL_13]] to %[[VAL_14]] : !fir.ref> +// CHECK: return +// CHECK: } + +// CHECK-LABEL: func.func @addrof() { +// CHECK: %[[VAL_0:.*]] = fir.address_of(@returncomplex16) : (!fir.ref, !fir.real<16>>>) -> () +// CHECK: %[[VAL_1:.*]] = fir.address_of(@paramcomplex16) : (!fir.ref, !fir.real<16>>>) -> () +// CHECK: return +// CHECK: } diff --git a/flang/test/Fir/target.fir b/flang/test/Fir/target.fir new file mode 100644 index 0000000000000..39798d0a23581 --- /dev/null +++ b/flang/test/Fir/target.fir @@ -0,0 +1,142 @@ +// RUN: tco --target=i386-unknown-linux-gnu %s | FileCheck %s --check-prefix=I32 +// RUN: tco --target=x86_64-unknown-linux-gnu %s | FileCheck %s --check-prefix=X64 +// RUN: tco --target=aarch64-unknown-linux-gnu %s | FileCheck %s --check-prefix=AARCH64 +// RUN: tco --target=powerpc64le-unknown-linux-gnu %s | FileCheck %s --check-prefix=PPC + +// I32-LABEL: define i64 @gen4() +// X64-LABEL: define <2 x float> @gen4() +// AARCH64-LABEL: define { float, float } @gen4() +// PPC-LABEL: define { float, float } @gen4() +func.func @gen4() -> !fir.complex<4> { + %1 = fir.undefined !fir.complex<4> + %2 = arith.constant 2.0 : f32 + %3 = fir.convert %2 : (f32) -> !fir.real<4> + %c0 = arith.constant 0 : i32 + %4 = fir.insert_value %1, %3, [0 : index] : (!fir.complex<4>, !fir.real<4>) -> !fir.complex<4> + %c1 = arith.constant 1 : i32 + %5 = arith.constant -42.0 : f32 + %6 = fir.insert_value %4, %5, [1 : index] : (!fir.complex<4>, f32) -> !fir.complex<4> + // I32: store { float, float } { float 2.000000e+00, float -4.200000e+01 } + // I32: %[[load:.*]] = load i64, ptr + // I32: ret i64 %[[load]] + // X64: store { float, float } { float 2.000000e+00, float -4.200000e+01 } + // X64: %[[load:.*]] = load <2 x float>, ptr + // X64: ret <2 x float> %[[load]] + // AARCH64: ret { float, float } + // PPC: ret { float, float } + return %6 : !fir.complex<4> +} + +// I32-LABEL: define void @gen8(ptr sret({ double, double }) align 4 % +// X64-LABEL: define { double, double } @gen8() +// AARCH64-LABEL: define { double, double } @gen8() +// PPC-LABEL: define { double, double } @gen8() +func.func @gen8() -> !fir.complex<8> { + %1 = fir.undefined !fir.complex<8> + %2 = arith.constant 1.0 : f64 + %3 = arith.constant -4.0 : f64 + %c0 = arith.constant 0 : i32 + %4 = fir.insert_value %1, %3, [0 : index] : (!fir.complex<8>, f64) -> !fir.complex<8> + %c1 = arith.constant 1 : i32 + %5 = fir.insert_value %4, %2, [1 : index] : (!fir.complex<8>, f64) -> !fir.complex<8> + // I32: store { double, double } { double -4.000000e+00, double 1.000000e+00 } + // I64: store { double, double } { double -4.000000e+00, double 1.000000e+00 } + // I64: %[[load:.*]] = load { double, double } + // I64: ret { double, double } %[[load]] + // AARCH64: ret { double, double } + // PPC: ret { double, double } + return %5 : !fir.complex<8> +} + +// I32: declare void @sink4(ptr) +// X64: declare void @sink4(<2 x float>) +// AARCH64: declare void @sink4([2 x float]) +// PPC: declare void @sink4(float, float) +func.func private @sink4(!fir.complex<4>) -> () + +// I32: declare void @sink8(ptr) +// X64: declare void @sink8(double, double) +// AARCH64: declare void @sink8([2 x double]) +// PPC: declare void @sink8(double, double) +func.func private @sink8(!fir.complex<8>) -> () + +// I32-LABEL: define void @call4() +// X64-LABEL: define void @call4() +// AARCH64-LABEL: define void @call4() +func.func @call4() { + // I32: = call i64 @gen4() + // X64: = call <2 x float> @gen4() + // AARCH64: = call { float, float } @gen4() + // PPC: = call { float, float } @gen4() + %1 = fir.call @gen4() : () -> !fir.complex<4> + // I32: call void @sink4(ptr % + // X64: call void @sink4(<2 x float> % + // AARCH64: call void @sink4([2 x float] % + // PPC: call void @sink4(float %{{.*}}, float %{{.*}}) + fir.call @sink4(%1) : (!fir.complex<4>) -> () + return +} + +// I32-LABEL: define void @call8() +// X64-LABEL: define void @call8() +// AARCH64-LABEL: define void @call8() +func.func @call8() { + // I32: call void @gen8(ptr % + // X64: = call { double, double } @gen8() + // AARCH64: = call { double, double } @gen8() + // PPC: = call { double, double } @gen8() + %1 = fir.call @gen8() : () -> !fir.complex<8> + // I32: call void @sink8(ptr % + // X64: call void @sink8(double %4, double %5) + // AARCH64: call void @sink8([2 x double] % + // PPC: call void @sink8(double %{{.*}}, double %{{.*}}) + fir.call @sink8(%1) : (!fir.complex<8>) -> () + return +} + +// I32-LABEL: define i64 @char1lensum(ptr %0, ptr %1, i32 %2, i32 %3) +// X64-LABEL: define i64 @char1lensum(ptr %0, ptr %1, i64 %2, i64 %3) +// PPC-LABEL: define i64 @char1lensum(ptr %0, ptr %1, i64 %2, i64 %3) +func.func @char1lensum(%arg0 : !fir.boxchar<1>, %arg1 : !fir.boxchar<1>) -> i64 { + // X64-DAG: %[[p0:.*]] = insertvalue { ptr, i64 } undef, ptr %1, 0 + // X64-DAG: = insertvalue { ptr, i64 } %[[p0]], i64 %3, 1 + // X64-DAG: %[[p1:.*]] = insertvalue { ptr, i64 } undef, ptr %0, 0 + // X64-DAG: = insertvalue { ptr, i64 } %[[p1]], i64 %2, 1 + %1:2 = fir.unboxchar %arg0 : (!fir.boxchar<1>) -> (!fir.ref>, i64) + %2:2 = fir.unboxchar %arg1 : (!fir.boxchar<1>) -> (!fir.ref>, i64) + // I32: %[[add:.*]] = add i64 % + // X64: %[[add:.*]] = add i64 % + %3 = arith.addi %1#1, %2#1 : i64 + // I32: ret i64 %[[add]] + // X64: ret i64 %[[add]] + return %3 : i64 +} + +// I32-LABEL: define void @char1copy(ptr sret(i8) %0, i32 %1, ptr %2, i32 %3) +// I64-LABEL: define void @char1copy(ptr sret(i8) %0, i64 %1, ptr %2, i64 %3) +// PPC-LABEL: define void @char1copy(ptr sret(i8) %0, i64 %1, ptr %2, i64 %3) +func.func @char1copy(%arg0 : !fir.boxchar<1> {llvm.sret}, %arg1 : !fir.boxchar<1>) { + // I32-DAG: %[[p0:.*]] = insertvalue { ptr, i32 } undef, ptr %2, 0 + // I32-DAG: = insertvalue { ptr, i32 } %[[p0]], i32 %3, 1 + // I32-DAG: %[[p1:.*]] = insertvalue { ptr, i32 } undef, ptr %0, 0 + // I32-DAG: = insertvalue { ptr, i32 } %[[p1]], i32 %1, 1 + // X64-DAG: %[[p0:.*]] = insertvalue { ptr, i64 } undef, ptr %2, 0 + // X64-DAG: = insertvalue { ptr, i64 } %[[p0]], i64 %3, 1 + // X64-DAG: %[[p1:.*]] = insertvalue { ptr, i64 } undef, ptr %0, 0 + // X64-DAG: = insertvalue { ptr, i64 } %[[p1]], i64 %1, 1 + %1:2 = fir.unboxchar %arg0 : (!fir.boxchar<1>) -> (!fir.ref>>, i64) + %2:2 = fir.unboxchar %arg1 : (!fir.boxchar<1>) -> (!fir.ref>>, i64) + %c0 = arith.constant 0 : index + %c1 = arith.constant 1 : index + %3 = fir.convert %1#1 : (i64) -> index + %last = arith.subi %3, %c1 : index + fir.do_loop %i = %c0 to %last step %c1 { + %in_pos = fir.coordinate_of %2#0, %i : (!fir.ref>>, index) -> !fir.ref> + %out_pos = fir.coordinate_of %1#0, %i : (!fir.ref>>, index) -> !fir.ref> + %ch = fir.load %in_pos : !fir.ref> + fir.store %ch to %out_pos : !fir.ref> + } + // I32: ret void + // X64: ret void + return +} diff --git a/flang/test/Fir/type-descriptor.fir b/flang/test/Fir/type-descriptor.fir new file mode 100644 index 0000000000000..c5362090e659f --- /dev/null +++ b/flang/test/Fir/type-descriptor.fir @@ -0,0 +1,29 @@ +// Test mapping between fir types and their runtime type descriptor objects. +// RUN: tco -o - %s | FileCheck %s + +// Test mapping of derived type following the internal name ABI (i.e. that could come from an actual +// compiled Fortran program). + +!sometype = !fir.type<_QFfooTsometype{num:i32,values:!fir.box>>}> +fir.global internal @_QFfooE.dt.sometype constant : i8 + +fir.global internal @_QFfooEx : !fir.box> { + %0 = fir.zero_bits !fir.heap + %1 = fir.embox %0 : (!fir.heap) -> !fir.box> + fir.has_value %1 : !fir.box> +} +// CHECK: @_QFfooEx = internal global { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] } +// CHECK-SAME: { ptr null, i64 ptrtoint (ptr getelementptr (%_QFfooTsometype, ptr null, i32 1) to i64), +// CHECK-SAME: i32 20180515, i8 0, i8 42, i8 2, i8 1, ptr @_QFfooE.dt.sometype, [1 x i64] undef } + +!some_pdt_type = !fir.type<_QFfooTsome_pdt_typeK42K43{num:i32,values:!fir.box>>}> +fir.global internal @_QFfooE.dt.some_pdt_type.42.43 constant : i8 + +fir.global internal @_QFfooEx2 : !fir.box> { + %0 = fir.zero_bits !fir.heap + %1 = fir.embox %0 : (!fir.heap) -> !fir.box> + fir.has_value %1 : !fir.box> +} +// CHECK: @_QFfooEx2 = internal global { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] } +// CHECK-SAME: { ptr null, i64 ptrtoint (ptr getelementptr (%_QFfooTsome_pdt_typeK42K43, ptr null, i32 1) to i64), +// CHECK-SAME: i32 20180515, i8 0, i8 42, i8 2, i8 1, ptr @_QFfooE.dt.some_pdt_type.42.43, [1 x i64] undef } diff --git a/flang/test/Fir/undo-complex-pattern.fir b/flang/test/Fir/undo-complex-pattern.fir new file mode 100644 index 0000000000000..853579b4bf5b9 --- /dev/null +++ b/flang/test/Fir/undo-complex-pattern.fir @@ -0,0 +1,98 @@ +// Test regrouping of + and - operations on complex components into complex operations +// RUN: fir-opt --canonicalize %s | FileCheck %s + + +// CHECK-LABEL: @add +func.func @add(%z: !fir.ref>, %z1 : !fir.complex<8>, %z2 : !fir.complex<8>) { + %c0 = arith.constant 0 : index + %c1 = arith.constant 1 : index + %real1 = fir.extract_value %z1, [0 : index] : (!fir.complex<8>) -> f64 + %imag1 = fir.extract_value %z1, [1 : index] : (!fir.complex<8>) -> f64 + %real2 = fir.extract_value %z2, [0 : index] : (!fir.complex<8>) -> f64 + %imag2 = fir.extract_value %z2, [1 : index] : (!fir.complex<8>) -> f64 + + // CHECK-LABEL: fir.addc + %real = arith.addf %real1, %real2 : f64 + %imag = arith.addf %imag1, %imag2 : f64 + %undef = fir.undefined !fir.complex<8> + %insert_real = fir.insert_value %undef, %real, [0 : index] : (!fir.complex<8>, f64) -> !fir.complex<8> + %insert_imag = fir.insert_value %insert_real, %imag, [1 : index] : (!fir.complex<8>, f64) -> !fir.complex<8> + fir.store %insert_imag to %z : !fir.ref> + return +} + +// CHECK-LABEL: @sub +func.func @sub(%z: !fir.ref>, %z1 : !fir.complex<8>, %z2 : !fir.complex<8>) { + %c0 = arith.constant 0 : index + %c1 = arith.constant 1 : index + %real1 = fir.extract_value %z1, [0 : index] : (!fir.complex<8>) -> f64 + %imag1 = fir.extract_value %z1, [1 : index] : (!fir.complex<8>) -> f64 + %real2 = fir.extract_value %z2, [0 : index] : (!fir.complex<8>) -> f64 + %imag2 = fir.extract_value %z2, [1 : index] : (!fir.complex<8>) -> f64 + + // CHECK-LABEL: fir.subc + %real = arith.subf %real1, %real2 : f64 + %imag = arith.subf %imag1, %imag2 : f64 + %undef = fir.undefined !fir.complex<8> + %insert_real = fir.insert_value %undef, %real, [0 : index] : (!fir.complex<8>, f64) -> !fir.complex<8> + %insert_imag = fir.insert_value %insert_real, %imag, [1 : index] : (!fir.complex<8>, f64) -> !fir.complex<8> + fir.store %insert_imag to %z : !fir.ref> + return +} + +// CHECK-LABEL: @undefOpHiddenByBranch +func.func @undefOpHiddenByBranch(%z: !fir.ref>, %b: i1) { + %c0 = arith.constant 0 : index + %c1 = arith.constant 1 : index + cf.cond_br %b, ^bb1, ^bb2 +^bb1: // pred: ^bb0 + %u1 = fir.undefined !fir.complex<8> + %z1l = fir.call @bar1() : () -> !fir.complex<8> + %z1r = fir.call @bar1() : () -> !fir.complex<8> + cf.br ^bb3(%u1, %z1l, %z1r : !fir.complex<8>, !fir.complex<8>, !fir.complex<8>) +^bb2: // pred: ^bb0 + %u2 = fir.undefined !fir.complex<8> + %z2l = fir.call @bar2() : () -> !fir.complex<8> + %z2r = fir.call @bar2() : () -> !fir.complex<8> + cf.br ^bb3(%u2, %z2l, %z2r : !fir.complex<8>, !fir.complex<8>, !fir.complex<8>) + +// CHECK: ^bb3(%[[z1:.*]]: !fir.complex<8>, %[[z2:.*]]: !fir.complex<8>): // 2 preds: ^bb1, ^bb2 +// CHECK: fir.addc %[[z1]], %[[z2]] : !fir.complex<8> + +^bb3(%undef : !fir.complex<8>, %z1 : !fir.complex<8>, %z2 : !fir.complex<8>): // 2 preds: ^bb1, ^bb2 + %real1 = fir.extract_value %z1, [0 : index] : (!fir.complex<8>) -> f64 + %imag1 = fir.extract_value %z1, [1 : index] : (!fir.complex<8>) -> f64 + %real2 = fir.extract_value %z2, [0 : index] : (!fir.complex<8>) -> f64 + %imag2 = fir.extract_value %z2, [1 : index] : (!fir.complex<8>) -> f64 + %real = arith.addf %real1, %real2 : f64 + %imag = arith.addf %imag1, %imag2 : f64 + %insert_real = fir.insert_value %undef, %real, [0 : index] : (!fir.complex<8>, f64) -> !fir.complex<8> + %insert_imag = fir.insert_value %insert_real, %imag, [1 : index] : (!fir.complex<8>, f64) -> !fir.complex<8> + fir.store %insert_imag to %z : !fir.ref> + return +} +func.func private @bar1() -> !fir.complex<8> +func.func private @bar2() -> !fir.complex<8> + +// CHECK-LABEL: @close_but_bad_pattern +func.func @close_but_bad_pattern(%z: !fir.ref>, %z1 : !fir.complex<8>, %z2 : !fir.complex<8>) { + %c0 = arith.constant 0 : index + %c1 = arith.constant 1 : index + %real1 = fir.extract_value %z1, [0 : index] : (!fir.complex<8>) -> f64 + // extracting %c0 instead of %c1 + %imag1 = fir.extract_value %z1, [0 : index] : (!fir.complex<8>) -> f64 + %real2 = fir.extract_value %z2, [0 : index] : (!fir.complex<8>) -> f64 + %imag2 = fir.extract_value %z2, [1 : index] : (!fir.complex<8>) -> f64 + // CHECK: arith.subf + // CHECK: subf + %real = arith.subf %real1, %real2 : f64 + %imag = arith.subf %imag1, %imag2 : f64 + %undef = fir.undefined !fir.complex<8> + // CHECK: %[[insert1:.*]] = fir.insert_value %{{.*}}, %{{.*}}, [0 + // CHECK: %[[insert2:.*]] = fir.insert_value %[[insert1]], %{{.*}}, [1 + %insert_real = fir.insert_value %undef, %real, [0 : index] : (!fir.complex<8>, f64) -> !fir.complex<8> + %insert_imag = fir.insert_value %insert_real, %imag, [1 : index] : (!fir.complex<8>, f64) -> !fir.complex<8> + // CHECK: fir.store %[[insert2]] to {{.*}} + fir.store %insert_imag to %z : !fir.ref> + return +}