@@ -2889,9 +2889,82 @@ static void genAtomicRead(lower::AbstractConverter &converter,
2889
2889
fir::getBase (converter.genExprAddr (fromExpr, stmtCtx));
2890
2890
mlir::Value toAddress = fir::getBase (converter.genExprAddr (
2891
2891
*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);
2895
2968
}
2896
2969
2897
2970
// / Processes an atomic construct with update clause.
@@ -2976,6 +3049,10 @@ static void genAtomicCapture(lower::AbstractConverter &converter,
2976
3049
mlir::Type stmt2VarType =
2977
3050
fir::getBase (converter.genExprValue (assign2.lhs , stmtCtx)).getType ();
2978
3051
3052
+ // Check if implicit type is needed
3053
+ if (stmt1VarType != stmt2VarType)
3054
+ TODO (loc, " atomic capture requiring implicit type casts" );
3055
+
2979
3056
mlir::Operation *atomicCaptureOp = nullptr ;
2980
3057
mlir::IntegerAttr hint = nullptr ;
2981
3058
mlir::omp::ClauseMemoryOrderKindAttr memoryOrder = nullptr ;
0 commit comments