Skip to content

Commit c54efad

Browse files
committed
[flang] introduce fir.copy to avoid load store of aggregates
1 parent 50a0931 commit c54efad

File tree

6 files changed

+168
-5
lines changed

6 files changed

+168
-5
lines changed

flang/include/flang/Optimizer/Dialect/FIROps.td

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,44 @@ def fir_StoreOp : fir_Op<"store", [FirAliasTagOpInterface]> {
342342
}];
343343
}
344344

345+
def fir_CopyOp : fir_Op<"copy", []> {
346+
let summary = "copy constant size memory";
347+
348+
let description = [{
349+
Copy the memory from a source with compile time constant size to
350+
a destination of the same type.
351+
352+
This is meant to be used for aggregate types where load and store
353+
are not appropriate to make a copy because LLVM is not meant to
354+
handle load and store of "big" aggregates.
355+
356+
Its "no_overlap" attribute allows indicating that the source and destination
357+
are known to not overlap at compile time.
358+
359+
```
360+
!t =!fir.type<t{x:!fir.array<1000xi32>}>
361+
fir.copy %x to %y : !fir.ref<!t>, !fir.ref<!t>
362+
```
363+
TODO: add FirAliasTagOpInterface to carry TBAA.
364+
}];
365+
366+
let arguments = (ins Arg<AnyReferenceLike, "", [MemRead]>:$source,
367+
Arg<AnyReferenceLike, "", [MemWrite]>:$destination,
368+
OptionalAttr<UnitAttr>:$no_overlap);
369+
370+
let builders = [OpBuilder<(ins "mlir::Value":$source,
371+
"mlir::Value":$destination,
372+
CArg<"bool", "false">:$no_overlap)>];
373+
374+
let assemblyFormat = [{
375+
$source `to` $destination (`no_overlap` $no_overlap^)?
376+
attr-dict `:` type(operands)
377+
}];
378+
379+
let hasVerifier = 1;
380+
}
381+
382+
345383
def fir_SaveResultOp : fir_Op<"save_result", [AttrSizedOperandSegments]> {
346384
let summary = [{
347385
save an array, box, or record function result SSA-value to a memory location

flang/lib/Optimizer/CodeGen/CodeGen.cpp

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3539,6 +3539,36 @@ struct StoreOpConversion : public fir::FIROpConversion<fir::StoreOp> {
35393539
}
35403540
};
35413541

3542+
/// `fir.copy` --> `llvm.memcpy` or `llvm.memmove`
3543+
struct CopyOpConversion : public fir::FIROpConversion<fir::CopyOp> {
3544+
using FIROpConversion::FIROpConversion;
3545+
3546+
llvm::LogicalResult
3547+
matchAndRewrite(fir::CopyOp copy, OpAdaptor adaptor,
3548+
mlir::ConversionPatternRewriter &rewriter) const override {
3549+
mlir::Location loc = copy.getLoc();
3550+
mlir::Value llvmSource = adaptor.getSource();
3551+
mlir::Value llvmDestination = adaptor.getDestination();
3552+
mlir::Type i64Ty = mlir::IntegerType::get(rewriter.getContext(), 64);
3553+
mlir::Type copyTy = fir::unwrapRefType(copy.getSource().getType());
3554+
mlir::Value copySize =
3555+
genTypeStrideInBytes(loc, i64Ty, rewriter, convertType(copyTy));
3556+
3557+
mlir::LLVM::AliasAnalysisOpInterface newOp;
3558+
if (copy.getNoOverlap())
3559+
newOp = rewriter.create<mlir::LLVM::MemcpyOp>(
3560+
loc, llvmDestination, llvmSource, copySize, /*isVolatile=*/false);
3561+
else
3562+
newOp = rewriter.create<mlir::LLVM::MemmoveOp>(
3563+
loc, llvmDestination, llvmSource, copySize, /*isVolatile=*/false);
3564+
3565+
// TODO: propagate TBAA once FirAliasTagOpInterface added to CopyOp.
3566+
attachTBAATag(newOp, copyTy, copyTy, nullptr);
3567+
rewriter.eraseOp(copy);
3568+
return mlir::success();
3569+
}
3570+
};
3571+
35423572
namespace {
35433573

35443574
/// Convert `fir.unboxchar` into two `llvm.extractvalue` instructions. One for
@@ -4142,11 +4172,11 @@ void fir::populateFIRToLLVMConversionPatterns(
41424172
BoxOffsetOpConversion, BoxProcHostOpConversion, BoxRankOpConversion,
41434173
BoxTypeCodeOpConversion, BoxTypeDescOpConversion, CallOpConversion,
41444174
CmpcOpConversion, ConvertOpConversion, CoordinateOpConversion,
4145-
DTEntryOpConversion, DeclareOpConversion, DivcOpConversion,
4146-
EmboxOpConversion, EmboxCharOpConversion, EmboxProcOpConversion,
4147-
ExtractValueOpConversion, FieldIndexOpConversion, FirEndOpConversion,
4148-
FreeMemOpConversion, GlobalLenOpConversion, GlobalOpConversion,
4149-
InsertOnRangeOpConversion, IsPresentOpConversion,
4175+
CopyOpConversion, DTEntryOpConversion, DeclareOpConversion,
4176+
DivcOpConversion, EmboxOpConversion, EmboxCharOpConversion,
4177+
EmboxProcOpConversion, ExtractValueOpConversion, FieldIndexOpConversion,
4178+
FirEndOpConversion, FreeMemOpConversion, GlobalLenOpConversion,
4179+
GlobalOpConversion, InsertOnRangeOpConversion, IsPresentOpConversion,
41504180
LenParamIndexOpConversion, LoadOpConversion, MulcOpConversion,
41514181
NegcOpConversion, NoReassocOpConversion, SelectCaseOpConversion,
41524182
SelectOpConversion, SelectRankOpConversion, SelectTypeOpConversion,

flang/lib/Optimizer/Dialect/FIROps.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3940,6 +3940,29 @@ void fir::StoreOp::build(mlir::OpBuilder &builder, mlir::OperationState &result,
39403940
build(builder, result, value, memref, {});
39413941
}
39423942

3943+
//===----------------------------------------------------------------------===//
3944+
// CopyOp
3945+
//===----------------------------------------------------------------------===//
3946+
3947+
void fir::CopyOp::build(mlir::OpBuilder &builder, mlir::OperationState &result,
3948+
mlir::Value source, mlir::Value destination,
3949+
bool noOverlap) {
3950+
mlir::UnitAttr noOverlapAttr =
3951+
noOverlap ? builder.getUnitAttr() : mlir::UnitAttr{};
3952+
build(builder, result, source, destination, noOverlapAttr);
3953+
}
3954+
3955+
llvm::LogicalResult fir::CopyOp::verify() {
3956+
mlir::Type sourceType = fir::unwrapRefType(getSource().getType());
3957+
mlir::Type destinationType = fir::unwrapRefType(getDestination().getType());
3958+
if (sourceType != destinationType)
3959+
return emitOpError("source and destination must have the same value type");
3960+
if (fir::hasDynamicSize(sourceType))
3961+
return emitOpError(
3962+
"source value type must have a compile time constant size");
3963+
return mlir::success();
3964+
}
3965+
39433966
//===----------------------------------------------------------------------===//
39443967
// StringLitOp
39453968
//===----------------------------------------------------------------------===//

flang/test/Fir/copy-codegen.fir

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Test fir.copy codegen.
2+
// RUN: fir-opt --fir-to-llvm-ir %s -o - | FileCheck %s
3+
4+
!t=!fir.type<sometype{i:!fir.array<9xi32>}>
5+
6+
module attributes {llvm.data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"} {
7+
8+
func.func @test_copy_1(%arg0: !fir.ref<!t>, %arg1: !fir.ref<!t>) {
9+
fir.copy %arg0 to %arg1 no_overlap : !fir.ref<!t>, !fir.ref<!t>
10+
return
11+
}
12+
// CHECK-LABEL: llvm.func @test_copy_1(
13+
// CHECK-SAME: %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !llvm.ptr,
14+
// CHECK-SAME: %[[VAL_1:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !llvm.ptr) {
15+
// CHECK: %[[VAL_2:.*]] = llvm.mlir.zero : !llvm.ptr
16+
// CHECK: %[[VAL_3:.*]] = llvm.getelementptr %[[VAL_2]][1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<"sometype", (array<9 x i32>)>
17+
// CHECK: %[[VAL_4:.*]] = llvm.ptrtoint %[[VAL_3]] : !llvm.ptr to i64
18+
// CHECK: "llvm.intr.memcpy"(%[[VAL_1]], %[[VAL_0]], %[[VAL_4]]) <{isVolatile = false}> : (!llvm.ptr, !llvm.ptr, i64) -> ()
19+
// CHECK: llvm.return
20+
// CHECK: }
21+
22+
func.func @test_copy_2(%arg0: !fir.ref<!t>, %arg1: !fir.ref<!t>) {
23+
fir.copy %arg0 to %arg1 : !fir.ref<!t>, !fir.ref<!t>
24+
return
25+
}
26+
// CHECK-LABEL: llvm.func @test_copy_2(
27+
// CHECK-SAME: %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !llvm.ptr,
28+
// CHECK-SAME: %[[VAL_1:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !llvm.ptr) {
29+
// CHECK: %[[VAL_2:.*]] = llvm.mlir.zero : !llvm.ptr
30+
// CHECK: %[[VAL_3:.*]] = llvm.getelementptr %[[VAL_2]][1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<"sometype", (array<9 x i32>)>
31+
// CHECK: %[[VAL_4:.*]] = llvm.ptrtoint %[[VAL_3]] : !llvm.ptr to i64
32+
// CHECK: "llvm.intr.memmove"(%[[VAL_1]], %[[VAL_0]], %[[VAL_4]]) <{isVolatile = false}> : (!llvm.ptr, !llvm.ptr, i64) -> ()
33+
// CHECK: llvm.return
34+
// CHECK: }
35+
}

flang/test/Fir/fir-ops.fir

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -933,3 +933,12 @@ func.func @test_call_arg_attrs_indirect(%arg0: i16, %arg1: (i16)-> i16) -> i16 {
933933
%0 = fir.call %arg1(%arg0) : (i16 {llvm.noundef, llvm.signext}) -> (i16 {llvm.signext})
934934
return %0 : i16
935935
}
936+
937+
// CHECK-LABEL: @test_copy(
938+
// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<!fir.type<sometype{i:i32}>>,
939+
// CHECK-SAME: %[[VAL_1:.*]]: !fir.ptr<!fir.type<sometype{i:i32}>>
940+
func.func @test_copy(%arg0: !fir.ref<!fir.type<sometype{i:i32}>>, %arg1: !fir.ptr<!fir.type<sometype{i:i32}>>) {
941+
// CHECK: fir.copy %[[VAL_0]] to %[[VAL_1]] no_overlap : !fir.ref<!fir.type<sometype{i:i32}>>, !fir.ptr<!fir.type<sometype{i:i32}>>
942+
fir.copy %arg0 to %arg1 no_overlap : !fir.ref<!fir.type<sometype{i:i32}>>, !fir.ptr<!fir.type<sometype{i:i32}>>
943+
return
944+
}

flang/test/Fir/invalid.fir

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,3 +1018,31 @@ func.func @bad_is_assumed_size(%arg0: !fir.ref<!fir.array<*:none>>) {
10181018
%1 = fir.is_assumed_size %arg0 : (!fir.ref<!fir.array<*:none>>) -> i1
10191019
return
10201020
}
1021+
1022+
// -----
1023+
1024+
!t=!fir.type<sometype{i:i32}>
1025+
!t2=!fir.type<sometype2{j:i32}>
1026+
func.func @bad_copy_1(%arg0: !fir.ref<!t>, %arg1: !fir.ref<!t2>) {
1027+
// expected-error@+1{{'fir.copy' op source and destination must have the same value type}}
1028+
fir.copy %arg0 to %arg1 no_overlap : !fir.ref<!t>, !fir.ref<!t2>
1029+
return
1030+
}
1031+
1032+
// -----
1033+
1034+
!t=!fir.type<sometype{i:i32}>
1035+
func.func @bad_copy_2(%arg0: !fir.ref<!t>, %arg1: !t) {
1036+
// expected-error@+1{{'fir.copy' op operand #0 must be any reference, but got '!fir.type<sometype{i:i32}>'}}
1037+
fir.copy %arg1 to %arg0 no_overlap : !t, !fir.ref<!t>
1038+
return
1039+
}
1040+
1041+
// -----
1042+
1043+
!t=!fir.array<?xi32>
1044+
func.func @test_copy(%arg0: !fir.ref<!t>, %arg1: !fir.ref<!t>) {
1045+
// expected-error@+1{{'fir.copy' op source value type must have a compile time constant size}}
1046+
fir.copy %arg0 to %arg1 no_overlap : !fir.ref<!t>, !fir.ref<!t>
1047+
return
1048+
}

0 commit comments

Comments
 (0)