From 17291019e34ecb2f56da007c50a9133718328ef2 Mon Sep 17 00:00:00 2001 From: Andrzej Janik Date: Wed, 3 Mar 2021 22:41:47 +0100 Subject: [PATCH] Implement atomic float add --- ptx/lib/zluda_ptx_impl.cl | 115 ++++++++++++++++++- ptx/lib/zluda_ptx_impl.spv | Bin 50100 -> 105668 bytes ptx/src/test/spirv_run/atom_add_float.ptx | 28 +++++ ptx/src/test/spirv_run/atom_add_float.spvtxt | 81 +++++++++++++ ptx/src/test/spirv_run/mod.rs | 1 + ptx/src/translate.rs | 78 +++++++++---- 6 files changed, 279 insertions(+), 24 deletions(-) create mode 100644 ptx/src/test/spirv_run/atom_add_float.ptx create mode 100644 ptx/src/test/spirv_run/atom_add_float.spvtxt diff --git a/ptx/lib/zluda_ptx_impl.cl b/ptx/lib/zluda_ptx_impl.cl index a878ddd..85958d5 100644 --- a/ptx/lib/zluda_ptx_impl.cl +++ b/ptx/lib/zluda_ptx_impl.cl @@ -1,7 +1,10 @@ // Every time this file changes it must te rebuilt: // ocloc -file zluda_ptx_impl.cl -64 -options "-cl-std=CL2.0 -Dcl_intel_bit_instructions" -out_dir . -device kbl -output_no_suffix -spv_only // Additionally you should strip names: -// spirv-opt --strip-debug zluda_ptx_impl.spv -o zluda_ptx_impl.spv +// spirv-opt --strip-debug zluda_ptx_impl.spv -o zluda_ptx_impl.spv --target-env=spv1.3 + +#pragma OPENCL EXTENSION cl_khr_int64_base_atomics : enable +#pragma OPENCL EXTENSION cl_khr_int64_extended_atomics : enable #define FUNC(NAME) __zluda_ptx_impl__ ## NAME @@ -25,6 +28,20 @@ return expected; \ } +#define atomic_add(NAME, SUCCESS, FAILURE, SCOPE, SPACE, TYPE, ATOMIC_TYPE, INT_TYPE) \ + TYPE FUNC(NAME)(SPACE TYPE* ptr, TYPE value) { \ + volatile SPACE ATOMIC_TYPE* atomic_ptr = (volatile SPACE ATOMIC_TYPE*)ptr; \ + union { \ + INT_TYPE int_view; \ + TYPE float_view; \ + } expected, desired; \ + expected.float_view = *ptr; \ + do { \ + desired.float_view = expected.float_view + value; \ + } while (!atomic_compare_exchange_strong_explicit(atomic_ptr, &expected.int_view, desired.int_view, SUCCESS, FAILURE, SCOPE)); \ + return expected.float_view; \ + } + // We are doing all this mess instead of accepting memory_order and memory_scope parameters // because ocloc emits broken (failing spirv-dis) SPIR-V when memory_order or memory_scope is a parameter @@ -120,6 +137,98 @@ atomic_dec(atom_acquire_sys_shared_dec, memory_order_acquire, memory_order_acqui atomic_dec(atom_release_sys_shared_dec, memory_order_release, memory_order_acquire, memory_scope_device, __local); atomic_dec(atom_acq_rel_sys_shared_dec, memory_order_acq_rel, memory_order_acquire, memory_scope_device, __local); +// atom.add.f32 +atomic_add(atom_relaxed_cta_generic_add_f32, memory_order_relaxed, memory_order_relaxed, memory_scope_work_group, , float, atomic_uint, uint); +atomic_add(atom_acquire_cta_generic_add_f32, memory_order_acquire, memory_order_acquire, memory_scope_work_group, , float, atomic_uint, uint); +atomic_add(atom_release_cta_generic_add_f32, memory_order_release, memory_order_acquire, memory_scope_work_group, , float, atomic_uint, uint); +atomic_add(atom_acq_rel_cta_generic_add_f32, memory_order_acq_rel, memory_order_acquire, memory_scope_work_group, , float, atomic_uint, uint); + +atomic_add(atom_relaxed_gpu_generic_add_f32, memory_order_relaxed, memory_order_relaxed, memory_scope_device, , float, atomic_uint, uint); +atomic_add(atom_acquire_gpu_generic_add_f32, memory_order_acquire, memory_order_acquire, memory_scope_device, , float, atomic_uint, uint); +atomic_add(atom_release_gpu_generic_add_f32, memory_order_release, memory_order_acquire, memory_scope_device, , float, atomic_uint, uint); +atomic_add(atom_acq_rel_gpu_generic_add_f32, memory_order_acq_rel, memory_order_acquire, memory_scope_device, , float, atomic_uint, uint); + +atomic_add(atom_relaxed_sys_generic_add_f32, memory_order_relaxed, memory_order_relaxed, memory_scope_device, , float, atomic_uint, uint); +atomic_add(atom_acquire_sys_generic_add_f32, memory_order_acquire, memory_order_acquire, memory_scope_device, , float, atomic_uint, uint); +atomic_add(atom_release_sys_generic_add_f32, memory_order_release, memory_order_acquire, memory_scope_device, , float, atomic_uint, uint); +atomic_add(atom_acq_rel_sys_generic_add_f32, memory_order_acq_rel, memory_order_acquire, memory_scope_device, , float, atomic_uint, uint); + +atomic_add(atom_relaxed_cta_global_add_f32, memory_order_relaxed, memory_order_relaxed, memory_scope_work_group, __global, float, atomic_uint, uint); +atomic_add(atom_acquire_cta_global_add_f32, memory_order_acquire, memory_order_acquire, memory_scope_work_group, __global, float, atomic_uint, uint); +atomic_add(atom_release_cta_global_add_f32, memory_order_release, memory_order_acquire, memory_scope_work_group, __global, float, atomic_uint, uint); +atomic_add(atom_acq_rel_cta_global_add_f32, memory_order_acq_rel, memory_order_acquire, memory_scope_work_group, __global, float, atomic_uint, uint); + +atomic_add(atom_relaxed_gpu_global_add_f32, memory_order_relaxed, memory_order_relaxed, memory_scope_device, __global, float, atomic_uint, uint); +atomic_add(atom_acquire_gpu_global_add_f32, memory_order_acquire, memory_order_acquire, memory_scope_device, __global, float, atomic_uint, uint); +atomic_add(atom_release_gpu_global_add_f32, memory_order_release, memory_order_acquire, memory_scope_device, __global, float, atomic_uint, uint); +atomic_add(atom_acq_rel_gpu_global_add_f32, memory_order_acq_rel, memory_order_acquire, memory_scope_device, __global, float, atomic_uint, uint); + +atomic_add(atom_relaxed_sys_global_add_f32, memory_order_relaxed, memory_order_relaxed, memory_scope_device, __global, float, atomic_uint, uint); +atomic_add(atom_acquire_sys_global_add_f32, memory_order_acquire, memory_order_acquire, memory_scope_device, __global, float, atomic_uint, uint); +atomic_add(atom_release_sys_global_add_f32, memory_order_release, memory_order_acquire, memory_scope_device, __global, float, atomic_uint, uint); +atomic_add(atom_acq_rel_sys_global_add_f32, memory_order_acq_rel, memory_order_acquire, memory_scope_device, __global, float, atomic_uint, uint); + +atomic_add(atom_relaxed_cta_shared_add_f32, memory_order_relaxed, memory_order_relaxed, memory_scope_work_group, __local, float, atomic_uint, uint); +atomic_add(atom_acquire_cta_shared_add_f32, memory_order_acquire, memory_order_acquire, memory_scope_work_group, __local, float, atomic_uint, uint); +atomic_add(atom_release_cta_shared_add_f32, memory_order_release, memory_order_acquire, memory_scope_work_group, __local, float, atomic_uint, uint); +atomic_add(atom_acq_rel_cta_shared_add_f32, memory_order_acq_rel, memory_order_acquire, memory_scope_work_group, __local, float, atomic_uint, uint); + +atomic_add(atom_relaxed_gpu_shared_add_f32, memory_order_relaxed, memory_order_relaxed, memory_scope_device, __local, float, atomic_uint, uint); +atomic_add(atom_acquire_gpu_shared_add_f32, memory_order_acquire, memory_order_acquire, memory_scope_device, __local, float, atomic_uint, uint); +atomic_add(atom_release_gpu_shared_add_f32, memory_order_release, memory_order_acquire, memory_scope_device, __local, float, atomic_uint, uint); +atomic_add(atom_acq_rel_gpu_shared_add_f32, memory_order_acq_rel, memory_order_acquire, memory_scope_device, __local, float, atomic_uint, uint); + +atomic_add(atom_relaxed_sys_shared_add_f32, memory_order_relaxed, memory_order_relaxed, memory_scope_device, __local, float, atomic_uint, uint); +atomic_add(atom_acquire_sys_shared_add_f32, memory_order_acquire, memory_order_acquire, memory_scope_device, __local, float, atomic_uint, uint); +atomic_add(atom_release_sys_shared_add_f32, memory_order_release, memory_order_acquire, memory_scope_device, __local, float, atomic_uint, uint); +atomic_add(atom_acq_rel_sys_shared_add_f32, memory_order_acq_rel, memory_order_acquire, memory_scope_device, __local, float, atomic_uint, uint); + +atomic_add(atom_relaxed_cta_generic_add_f64, memory_order_relaxed, memory_order_relaxed, memory_scope_work_group, , double, atomic_ulong, ulong); +atomic_add(atom_acquire_cta_generic_add_f64, memory_order_acquire, memory_order_acquire, memory_scope_work_group, , double, atomic_ulong, ulong); +atomic_add(atom_release_cta_generic_add_f64, memory_order_release, memory_order_acquire, memory_scope_work_group, , double, atomic_ulong, ulong); +atomic_add(atom_acq_rel_cta_generic_add_f64, memory_order_acq_rel, memory_order_acquire, memory_scope_work_group, , double, atomic_ulong, ulong); + +atomic_add(atom_relaxed_gpu_generic_add_f64, memory_order_relaxed, memory_order_relaxed, memory_scope_device, , double, atomic_ulong, ulong); +atomic_add(atom_acquire_gpu_generic_add_f64, memory_order_acquire, memory_order_acquire, memory_scope_device, , double, atomic_ulong, ulong); +atomic_add(atom_release_gpu_generic_add_f64, memory_order_release, memory_order_acquire, memory_scope_device, , double, atomic_ulong, ulong); +atomic_add(atom_acq_rel_gpu_generic_add_f64, memory_order_acq_rel, memory_order_acquire, memory_scope_device, , double, atomic_ulong, ulong); + +atomic_add(atom_relaxed_sys_generic_add_f64, memory_order_relaxed, memory_order_relaxed, memory_scope_device, , double, atomic_ulong, ulong); +atomic_add(atom_acquire_sys_generic_add_f64, memory_order_acquire, memory_order_acquire, memory_scope_device, , double, atomic_ulong, ulong); +atomic_add(atom_release_sys_generic_add_f64, memory_order_release, memory_order_acquire, memory_scope_device, , double, atomic_ulong, ulong); +atomic_add(atom_acq_rel_sys_generic_add_f64, memory_order_acq_rel, memory_order_acquire, memory_scope_device, , double, atomic_ulong, ulong); + +// atom.add.f64 +atomic_add(atom_relaxed_cta_global_add_f64, memory_order_relaxed, memory_order_relaxed, memory_scope_work_group, __global, double, atomic_ulong, ulong); +atomic_add(atom_acquire_cta_global_add_f64, memory_order_acquire, memory_order_acquire, memory_scope_work_group, __global, double, atomic_ulong, ulong); +atomic_add(atom_release_cta_global_add_f64, memory_order_release, memory_order_acquire, memory_scope_work_group, __global, double, atomic_ulong, ulong); +atomic_add(atom_acq_rel_cta_global_add_f64, memory_order_acq_rel, memory_order_acquire, memory_scope_work_group, __global, double, atomic_ulong, ulong); + +atomic_add(atom_relaxed_gpu_global_add_f64, memory_order_relaxed, memory_order_relaxed, memory_scope_device, __global, double, atomic_ulong, ulong); +atomic_add(atom_acquire_gpu_global_add_f64, memory_order_acquire, memory_order_acquire, memory_scope_device, __global, double, atomic_ulong, ulong); +atomic_add(atom_release_gpu_global_add_f64, memory_order_release, memory_order_acquire, memory_scope_device, __global, double, atomic_ulong, ulong); +atomic_add(atom_acq_rel_gpu_global_add_f64, memory_order_acq_rel, memory_order_acquire, memory_scope_device, __global, double, atomic_ulong, ulong); + +atomic_add(atom_relaxed_sys_global_add_f64, memory_order_relaxed, memory_order_relaxed, memory_scope_device, __global, double, atomic_ulong, ulong); +atomic_add(atom_acquire_sys_global_add_f64, memory_order_acquire, memory_order_acquire, memory_scope_device, __global, double, atomic_ulong, ulong); +atomic_add(atom_release_sys_global_add_f64, memory_order_release, memory_order_acquire, memory_scope_device, __global, double, atomic_ulong, ulong); +atomic_add(atom_acq_rel_sys_global_add_f64, memory_order_acq_rel, memory_order_acquire, memory_scope_device, __global, double, atomic_ulong, ulong); + +atomic_add(atom_relaxed_cta_shared_add_f64, memory_order_relaxed, memory_order_relaxed, memory_scope_work_group, __local, double, atomic_ulong, ulong); +atomic_add(atom_acquire_cta_shared_add_f64, memory_order_acquire, memory_order_acquire, memory_scope_work_group, __local, double, atomic_ulong, ulong); +atomic_add(atom_release_cta_shared_add_f64, memory_order_release, memory_order_acquire, memory_scope_work_group, __local, double, atomic_ulong, ulong); +atomic_add(atom_acq_rel_cta_shared_add_f64, memory_order_acq_rel, memory_order_acquire, memory_scope_work_group, __local, double, atomic_ulong, ulong); + +atomic_add(atom_relaxed_gpu_shared_add_f64, memory_order_relaxed, memory_order_relaxed, memory_scope_device, __local, double, atomic_ulong, ulong); +atomic_add(atom_acquire_gpu_shared_add_f64, memory_order_acquire, memory_order_acquire, memory_scope_device, __local, double, atomic_ulong, ulong); +atomic_add(atom_release_gpu_shared_add_f64, memory_order_release, memory_order_acquire, memory_scope_device, __local, double, atomic_ulong, ulong); +atomic_add(atom_acq_rel_gpu_shared_add_f64, memory_order_acq_rel, memory_order_acquire, memory_scope_device, __local, double, atomic_ulong, ulong); + +atomic_add(atom_relaxed_sys_shared_add_f64, memory_order_relaxed, memory_order_relaxed, memory_scope_device, __local, double, atomic_ulong, ulong); +atomic_add(atom_acquire_sys_shared_add_f64, memory_order_acquire, memory_order_acquire, memory_scope_device, __local, double, atomic_ulong, ulong); +atomic_add(atom_release_sys_shared_add_f64, memory_order_release, memory_order_acquire, memory_scope_device, __local, double, atomic_ulong, ulong); +atomic_add(atom_acq_rel_sys_shared_add_f64, memory_order_acq_rel, memory_order_acquire, memory_scope_device, __local, double, atomic_ulong, ulong); + uint FUNC(bfe_u32)(uint base, uint pos, uint len) { return intel_ubfe(base, pos, len); } @@ -136,11 +245,11 @@ long FUNC(bfe_s64)(long base, uint pos, uint len) { return intel_sbfe(base, pos, len); } -uint FUNC(bfi_b32)(uint base, uint insert, uint offset, uint count) { +uint FUNC(bfi_b32)(uint insert, uint base, uint offset, uint count) { return intel_bfi(base, insert, offset, count); } -ulong FUNC(bfi_b64)(ulong base, ulong insert, uint offset, uint count) { +ulong FUNC(bfi_b64)(ulong insert, ulong base, uint offset, uint count) { return intel_bfi(base, insert, offset, count); } diff --git a/ptx/lib/zluda_ptx_impl.spv b/ptx/lib/zluda_ptx_impl.spv index 8a2d6970af32987b1750771d168825f50eb83971..ca16447340e5429c794f3e82484be9f7b9022e2f 100644 GIT binary patch literal 105668 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 TqTj5@o2!R+@$XnnlllJ-yEEF8 delta 953 zcmZvayKWRg5JgY-tfv}~HfV$q5+E=E0f=x7p(Q8mXYdCkL`V}7ubdG&BVH16#3vXq z#s>4U0UIzFmdD!Y_Vn5U-;S&D6sO_5C|lxAGSZ99?QG47xM!*kEop)&VX9 z9pE~iIt&(9-S)!ViBk)AU3BY7J#x`4CAH(C+fC}Mk8UHWzK?Dtsarm}eW~H1kFoC4 zrw+sAi!*LGf9baC9mn&>;2a&s1_lJ+Jb^tUovE(p3hjGr!|C3QdyNN{6aXq7+v z{*XBzi-EQpgGd{aStfzD3Xmq+&Y { err: T, diff --git a/ptx/src/translate.rs b/ptx/src/translate.rs index 7566be8..3291ad5 100644 --- a/ptx/src/translate.rs +++ b/ptx/src/translate.rs @@ -1505,6 +1505,7 @@ fn extract_globals<'input, 'b>( d, a, "inc", + ast::SizedScalarType::U32, )); } Statement::Instruction(ast::Instruction::Atom( @@ -1526,6 +1527,44 @@ fn extract_globals<'input, 'b>( d, a, "dec", + ast::SizedScalarType::U32, + )); + } + Statement::Instruction(ast::Instruction::Atom( + ast::AtomDetails { + inner: + ast::AtomInnerDetails::Float { + op: ast::AtomFloatOp::Add, + typ, + }, + semantics, + scope, + space, + }, + a, + )) => { + let details = ast::AtomDetails { + inner: ast::AtomInnerDetails::Float { + op: ast::AtomFloatOp::Add, + typ, + }, + semantics, + scope, + space, + }; + let (op, typ) = match typ { + ast::FloatType::F32 => ("add_f32", ast::SizedScalarType::F32), + ast::FloatType::F64 => ("add_f64", ast::SizedScalarType::F64), + ast::FloatType::F16 => unreachable!(), + ast::FloatType::F16x2 => unreachable!(), + }; + local.push(to_ptx_impl_atomic_call( + id_def, + ptx_impl_imports, + details, + a, + op, + typ, )); } s => local.push(s), @@ -1696,6 +1735,7 @@ fn to_ptx_impl_atomic_call( details: ast::AtomDetails, arg: ast::Arg3, op: &'static str, + typ: ast::SizedScalarType, ) -> ExpandedStatement { let semantics = ptx_semantics_name(details.semantics); let scope = ptx_scope_name(details.scope); @@ -1710,15 +1750,14 @@ fn to_ptx_impl_atomic_call( ast::AtomSpace::Global => ast::PointerStateSpace::Global, ast::AtomSpace::Shared => ast::PointerStateSpace::Shared, }; + let scalar_typ = ast::ScalarType::from(typ); let fn_id = match ptx_impl_imports.entry(fn_name) { hash_map::Entry::Vacant(entry) => { let fn_id = id_defs.new_non_variable(None); let func_decl = ast::MethodDecl::Func::( vec![ast::FnArgument { align: None, - v_type: ast::FnArgumentType::Reg(ast::VariableRegType::Scalar( - ast::ScalarType::U32, - )), + v_type: ast::FnArgumentType::Reg(ast::VariableRegType::Scalar(scalar_typ)), name: id_defs.new_non_variable(None), array_init: Vec::new(), }], @@ -1727,17 +1766,14 @@ fn to_ptx_impl_atomic_call( ast::FnArgument { align: None, v_type: ast::FnArgumentType::Reg(ast::VariableRegType::Pointer( - ast::SizedScalarType::U32, - ptr_space, + typ, ptr_space, )), name: id_defs.new_non_variable(None), array_init: Vec::new(), }, ast::FnArgument { align: None, - v_type: ast::FnArgumentType::Reg(ast::VariableRegType::Scalar( - ast::ScalarType::U32, - )), + v_type: ast::FnArgumentType::Reg(ast::VariableRegType::Scalar(scalar_typ)), name: id_defs.new_non_variable(None), array_init: Vec::new(), }, @@ -1768,19 +1804,16 @@ fn to_ptx_impl_atomic_call( func: fn_id, ret_params: vec![( arg.dst, - ast::FnArgumentType::Reg(ast::VariableRegType::Scalar(ast::ScalarType::U32)), + ast::FnArgumentType::Reg(ast::VariableRegType::Scalar(scalar_typ)), )], param_list: vec![ ( arg.src1, - ast::FnArgumentType::Reg(ast::VariableRegType::Pointer( - ast::SizedScalarType::U32, - ptr_space, - )), + ast::FnArgumentType::Reg(ast::VariableRegType::Pointer(typ, ptr_space)), ), ( arg.src2, - ast::FnArgumentType::Reg(ast::VariableRegType::Scalar(ast::ScalarType::U32)), + ast::FnArgumentType::Reg(ast::VariableRegType::Scalar(scalar_typ)), ), ], }) @@ -1963,14 +1996,13 @@ fn to_ptx_impl_bfi_call( arg.dst, ast::FnArgumentType::Reg(ast::VariableRegType::Scalar(typ.into())), )], - // Note, for some reason PTX and SPIR-V order base&insert arguments differently param_list: vec![ ( - arg.src2, + arg.src1, ast::FnArgumentType::Reg(ast::VariableRegType::Scalar(typ.into())), ), ( - arg.src1, + arg.src2, ast::FnArgumentType::Reg(ast::VariableRegType::Scalar(typ.into())), ), ( @@ -3476,8 +3508,12 @@ fn emit_atom( }; (spirv_op, typ.into()) } - // TODO: Hardware is capable of this, implement it through builtin - ast::AtomInnerDetails::Float { .. } => todo!(), + ast::AtomInnerDetails::Float { op, typ } => { + let spirv_op: fn(&mut dr::Builder, _, _, _, _, _, _) -> _ = match op { + ast::AtomFloatOp::Add => dr::Builder::atomic_f_add_ext, + }; + (spirv_op, typ.into()) + } }; let result_type = map.get_or_add_scalar(builder, typ); let memory_const = map.get_or_add_constant( @@ -4287,8 +4323,8 @@ fn emit_implicit_conversion( } (TypeKind::Scalar, TypeKind::Scalar, ConversionKind::SignExtend) => { let result_type = map.get_or_add(builder, SpirvType::from(cv.to.clone())); - builder.s_convert(result_type , Some(cv.dst), cv.src)?; - }, + builder.s_convert(result_type, Some(cv.dst), cv.src)?; + } (TypeKind::Vector, TypeKind::Scalar, ConversionKind::Default) | (TypeKind::Scalar, TypeKind::Array, ConversionKind::Default) | (TypeKind::Array, TypeKind::Scalar, ConversionKind::Default) => {