@@ -2889,9 +2889,82 @@ static void genAtomicRead(lower::AbstractConverter &converter,
28892889 fir::getBase (converter.genExprAddr (fromExpr, stmtCtx));
28902890 mlir::Value toAddress = fir::getBase (converter.genExprAddr (
28912891 *semantics::GetExpr (assignmentStmtVariable), stmtCtx));
2892- genAtomicCaptureStatement (converter, fromAddress, toAddress,
2893- leftHandClauseList, rightHandClauseList,
2894- elementType, loc);
2892+
2893+ if (fromAddress.getType () != toAddress.getType ()) {
2894+ // Emit an implicit cast. Different yet compatible types on
2895+ // omp.atomic.read constitute valid Fortran. The OMPIRBuilder will
2896+ // emit atomic instructions (on primitive types) and `__atomic_load`
2897+ // libcall (on complex type) without explicitly converting
2898+ // between such compatible types. The OMPIRBuilder relies on the
2899+ // frontend to resolve such inconsistencies between `omp.atomic.read `
2900+ // operand types. Similar inconsistencies between operand types in
2901+ // `omp.atomic.write` are resolved through implicit casting by use of typed
2902+ // assignment (i.e. `evaluate::Assignment`). However, use of typed
2903+ // assignment in `omp.atomic.read` (of form `v = x`) leads to an unsafe,
2904+ // non-atomic load of `x` into a temporary `alloca`, followed by an atomic
2905+ // read of form `v = alloca`. Hence, it is needed to perform a custom
2906+ // implicit cast.
2907+
2908+ // An atomic read of form `v = x` would (without implicit casting)
2909+ // lower to `omp.atomic.read %v = %x : !fir.ref<type1>, !fir.ref<type2>,
2910+ // type2`. This implicit casting will rather generate the following FIR:
2911+ //
2912+ // %alloca = fir.alloca type2
2913+ // omp.atomic.read %alloca = %x : !fir.ref<type2>, !fir.ref<type2>, type2
2914+ // %load = fir.load %alloca : !fir.ref<type2>
2915+ // %cvt = fir.convert %load : (type2) -> type1
2916+ // fir.store %cvt to %v : !fir.ref<type1>
2917+
2918+ // These sequence of operations is thread-safe since each thread allocates
2919+ // the `alloca` in its stack, and performs `%alloca = %x` atomically. Once
2920+ // safely read, each thread performs the implicit cast on the local
2921+ // `alloca`, and writes the final result to `%v`.
2922+ mlir::Type toType = fir::unwrapRefType (toAddress.getType ());
2923+ mlir::Type fromType = fir::unwrapRefType (fromAddress.getType ());
2924+ fir::FirOpBuilder &builder = converter.getFirOpBuilder ();
2925+ auto oldIP = builder.saveInsertionPoint ();
2926+ builder.setInsertionPointToStart (builder.getAllocaBlock ());
2927+ mlir::Value alloca = builder.create <fir::AllocaOp>(
2928+ loc, fromType); // Thread scope `alloca` to atomically read `%x`.
2929+ builder.restoreInsertionPoint (oldIP);
2930+ genAtomicCaptureStatement (converter, fromAddress, alloca,
2931+ leftHandClauseList, rightHandClauseList,
2932+ elementType, loc);
2933+ auto load = builder.create <fir::LoadOp>(loc, alloca);
2934+ if (fir::isa_complex (fromType) && !fir::isa_complex (toType)) {
2935+ // Emit an additional `ExtractValueOp` if `fromAddress` is of complex
2936+ // type, but `toAddress` is not.
2937+ auto extract = builder.create <fir::ExtractValueOp>(
2938+ loc, mlir::cast<mlir::ComplexType>(fromType).getElementType (), load,
2939+ builder.getArrayAttr (
2940+ builder.getIntegerAttr (builder.getIndexType (), 0 )));
2941+ auto cvt = builder.create <fir::ConvertOp>(loc, toType, extract);
2942+ builder.create <fir::StoreOp>(loc, cvt, toAddress);
2943+ } else if (!fir::isa_complex (fromType) && fir::isa_complex (toType)) {
2944+ // Emit an additional `InsertValueOp` if `toAddress` is of complex
2945+ // type, but `fromAddress` is not.
2946+ mlir::Value undef = builder.create <fir::UndefOp>(loc, toType);
2947+ mlir::Type complexEleTy =
2948+ mlir::cast<mlir::ComplexType>(toType).getElementType ();
2949+ mlir::Value cvt = builder.create <fir::ConvertOp>(loc, complexEleTy, load);
2950+ mlir::Value zero = builder.createRealZeroConstant (loc, complexEleTy);
2951+ mlir::Value idx0 = builder.create <fir::InsertValueOp>(
2952+ loc, toType, undef, cvt,
2953+ builder.getArrayAttr (
2954+ builder.getIntegerAttr (builder.getIndexType (), 0 )));
2955+ mlir::Value idx1 = builder.create <fir::InsertValueOp>(
2956+ loc, toType, idx0, zero,
2957+ builder.getArrayAttr (
2958+ builder.getIntegerAttr (builder.getIndexType (), 1 )));
2959+ builder.create <fir::StoreOp>(loc, idx1, toAddress);
2960+ } else {
2961+ auto cvt = builder.create <fir::ConvertOp>(loc, toType, load);
2962+ builder.create <fir::StoreOp>(loc, cvt, toAddress);
2963+ }
2964+ } else
2965+ genAtomicCaptureStatement (converter, fromAddress, toAddress,
2966+ leftHandClauseList, rightHandClauseList,
2967+ elementType, loc);
28952968}
28962969
28972970// / Processes an atomic construct with update clause.
@@ -2976,6 +3049,10 @@ static void genAtomicCapture(lower::AbstractConverter &converter,
29763049 mlir::Type stmt2VarType =
29773050 fir::getBase (converter.genExprValue (assign2.lhs , stmtCtx)).getType ();
29783051
3052+ // Check if implicit type is needed
3053+ if (stmt1VarType != stmt2VarType)
3054+ TODO (loc, " atomic capture requiring implicit type casts" );
3055+
29793056 mlir::Operation *atomicCaptureOp = nullptr ;
29803057 mlir::IntegerAttr hint = nullptr ;
29813058 mlir::omp::ClauseMemoryOrderKindAttr memoryOrder = nullptr ;
0 commit comments