diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td --- a/flang/include/flang/Optimizer/Dialect/FIROps.td +++ b/flang/include/flang/Optimizer/Dialect/FIROps.td @@ -2176,18 +2176,35 @@ let summary = "insert sub-value into a range on an existing sequence"; let description = [{ - Insert a constant value into an entity with an array type. Returns a - new ssa value where the range of offsets from the original array have been - replaced with the constant. The result is an array type entity. + Insert copies of a value into an entity with an array type. + Returns a new ssa value with the same type as the original entity. + The values are inserted at a contiguous range of indices in Fortran + row-to-column element order as specified by lower and upper bound + coordinates. + + ```mlir + %a = fir.undefined !fir.array<10x10xf32> + %c = constant 3.0 : f32 + %1 = fir.insert_on_range %a, %c, [0 : index, 7 : index, 0 : index, 2 : index] : (!fir.array<10x10xf32>, f32) -> !fir.array<10x10xf32> + ``` + + The first 28 elements of %1, with coordinates from (0,0) to (7,2), have + the value 3.0. }]; - let arguments = (ins fir_SequenceType:$seq, AnyType:$val, - Variadic:$coor); + let arguments = (ins fir_SequenceType:$seq, AnyType:$val, ArrayAttr:$coor); let results = (outs fir_SequenceType); let assemblyFormat = [{ - operands attr-dict `:` functional-type(operands, results) + $seq `,` $val `,` $coor attr-dict `:` functional-type(operands, results) }]; + + let builders = [ + OpBuilder<(ins "mlir::Type":$rty, "mlir::Value":$adt, "mlir::Value":$val, + "llvm::ArrayRef":$vcoor)> + ]; + + let verifier = [{ return ::verify(*this); }]; } def fir_LenParamIndexOp : fir_OneResultOp<"len_param_index", [NoSideEffect]> { diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp --- a/flang/lib/Optimizer/Dialect/FIROps.cpp +++ b/flang/lib/Optimizer/Dialect/FIROps.cpp @@ -658,6 +658,70 @@ return mlir::success(llvm::is_contained(validNames, linkage)); } +template +static void appendAsAttribute(llvm::SmallVectorImpl &attrs, + mlir::Value val) { + if (auto op = val.getDefiningOp()) { + if (auto cop = mlir::dyn_cast(op)) { + // append the integer constant value + if (auto iattr = cop.getValue().dyn_cast()) { + attrs.push_back(iattr); + return; + } + } else if (auto fld = mlir::dyn_cast(op)) { + if constexpr (AllowFields) { + // append the field name and the record type + attrs.push_back(fld.field_idAttr()); + attrs.push_back(fld.on_typeAttr()); + return; + } + } + } + llvm::report_fatal_error("cannot build Op with these arguments"); +} + +template +static mlir::ArrayAttr collectAsAttributes(mlir::MLIRContext *ctxt, + OperationState &result, + llvm::ArrayRef inds) { + llvm::SmallVector attrs; + for (auto v : inds) + appendAsAttribute(attrs, v); + assert(!attrs.empty()); + return mlir::ArrayAttr::get(ctxt, attrs); +} + +//===----------------------------------------------------------------------===// +// InsertOnRangeOp +//===----------------------------------------------------------------------===// + +void fir::InsertOnRangeOp::build(mlir::OpBuilder &builder, + OperationState &result, mlir::Type resTy, + mlir::Value aggVal, mlir::Value eleVal, + llvm::ArrayRef inds) { + auto aa = collectAsAttributes(builder.getContext(), result, inds); + build(builder, result, resTy, aggVal, eleVal, aa); +} + +/// Range bounds must be nonnegative, and the range must not be empty. +static mlir::LogicalResult verify(fir::InsertOnRangeOp op) { + if (op.coor().size() < 2 || op.coor().size() % 2 != 0) + return op.emitOpError("has uneven number of values in ranges"); + bool rangeIsKnownToBeNonempty = false; + for (auto i = op.coor().end(), b = op.coor().begin(); i != b;) { + int64_t ub = (*--i).cast().getInt(); + int64_t lb = (*--i).cast().getInt(); + if (lb < 0 || ub < 0) + return op.emitOpError("negative range bound"); + if (rangeIsKnownToBeNonempty) + continue; + if (lb > ub) + return op.emitOpError("empty range"); + rangeIsKnownToBeNonempty = lb < ub; + } + return mlir::success(); +} + //===----------------------------------------------------------------------===// // InsertValueOp //===----------------------------------------------------------------------===// diff --git a/flang/test/Fir/fir-ops.fir b/flang/test/Fir/fir-ops.fir --- a/flang/test/Fir/fir-ops.fir +++ b/flang/test/Fir/fir-ops.fir @@ -621,10 +621,10 @@ %c1_i32 = constant 9 : i32 // CHECK: [[ARR2:%.*]] = fir.zero_bits !fir.array<10xi32> - // CHECK: [[ARR3:%.*]] = fir.insert_on_range [[ARR2]], [[C1_I32]], [[C2]], [[C9]] : (!fir.array<10xi32>, i32, index, index) -> !fir.array<10xi32> + // CHECK: [[ARR3:%.*]] = fir.insert_on_range [[ARR2]], [[C1_I32]], [2 : index, 9 : index] : (!fir.array<10xi32>, i32) -> !fir.array<10xi32> // CHECK: fir.call @noret1([[ARR3]]) : (!fir.array<10xi32>) -> () %arr2 = fir.zero_bits !fir.array<10xi32> - %arr3 = fir.insert_on_range %arr2, %c1_i32, %c2, %c9 : (!fir.array<10xi32>, i32, index, index) -> !fir.array<10xi32> + %arr3 = fir.insert_on_range %arr2, %c1_i32, [2 : index, 9 : index] : (!fir.array<10xi32>, i32) -> !fir.array<10xi32> fir.call @noret1(%arr3) : (!fir.array<10xi32>) -> () // CHECK: [[SHAPE:%.*]] = fir.shape_shift [[INDXM:%.*]], [[INDXN:%.*]], [[INDXO:%.*]], [[INDXP:%.*]] : (index, index, index, index) -> !fir.shapeshift<2> diff --git a/flang/test/Fir/invalid.fir b/flang/test/Fir/invalid.fir --- a/flang/test/Fir/invalid.fir +++ b/flang/test/Fir/invalid.fir @@ -377,3 +377,43 @@ // expected-error@+1 {{'fir.result' op parent of result must have same arity}} fir.do_loop %i = %c1 to %c10 step %c1 -> index { } + +// ----- + +fir.global internal @_QEmultiarray : !fir.array<32x32xi32> { + %c0_i32 = constant 1 : i32 + %0 = fir.undefined !fir.array<32x32xi32> + // expected-error@+1 {{'fir.insert_on_range' op has uneven number of values in ranges}} + %2 = fir.insert_on_range %0, %c0_i32, [0 : index, 31 : index, 0 : index] : (!fir.array<32x32xi32>, i32) -> !fir.array<32x32xi32> + fir.has_value %2 : !fir.array<32x32xi32> +} + +// ----- + +fir.global internal @_QEmultiarray : !fir.array<32x32xi32> { + %c0_i32 = constant 1 : i32 + %0 = fir.undefined !fir.array<32x32xi32> + // expected-error@+1 {{'fir.insert_on_range' op has uneven number of values in ranges}} + %2 = fir.insert_on_range %0, %c0_i32, [0 : index] : (!fir.array<32x32xi32>, i32) -> !fir.array<32x32xi32> + fir.has_value %2 : !fir.array<32x32xi32> +} + +// ----- + +fir.global internal @_QEmultiarray : !fir.array<32x32xi32> { + %c0_i32 = constant 1 : i32 + %0 = fir.undefined !fir.array<32x32xi32> + // expected-error@+1 {{'fir.insert_on_range' op negative range bound}} + %2 = fir.insert_on_range %0, %c0_i32, [-1 : index, 0 : index] : (!fir.array<32x32xi32>, i32) -> !fir.array<32x32xi32> + fir.has_value %2 : !fir.array<32x32xi32> +} + +// ----- + +fir.global internal @_QEmultiarray : !fir.array<32x32xi32> { + %c0_i32 = constant 1 : i32 + %0 = fir.undefined !fir.array<32x32xi32> + // expected-error@+1 {{'fir.insert_on_range' op empty range}} + %2 = fir.insert_on_range %0, %c0_i32, [10 : index, 9 : index] : (!fir.array<32x32xi32>, i32) -> !fir.array<32x32xi32> + fir.has_value %2 : !fir.array<32x32xi32> +}