1625 lines
65 KiB
C++
1625 lines
65 KiB
C++
//===- OCLUtil.cpp - OCL Utilities ----------------------------------------===//
|
|
//
|
|
// The LLVM/SPIRV Translator
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
// copy of this software and associated documentation files (the "Software"),
|
|
// to deal with the Software without restriction, including without limitation
|
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
// and/or sell copies of the Software, and to permit persons to whom the
|
|
// Software is furnished to do so, subject to the following conditions:
|
|
//
|
|
// Redistributions of source code must retain the above copyright notice,
|
|
// this list of conditions and the following disclaimers.
|
|
// Redistributions in binary form must reproduce the above copyright notice,
|
|
// this list of conditions and the following disclaimers in the documentation
|
|
// and/or other materials provided with the distribution.
|
|
// Neither the names of Advanced Micro Devices, Inc., nor the names of its
|
|
// contributors may be used to endorse or promote products derived from this
|
|
// Software without specific prior written permission.
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH
|
|
// THE SOFTWARE.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements OCL utility functions.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "OCLUtil.h"
|
|
#include "SPIRVEntry.h"
|
|
#include "SPIRVFunction.h"
|
|
#include "SPIRVInstruction.h"
|
|
#include "SPIRVInternal.h"
|
|
#include "llvm/ADT/StringSwitch.h"
|
|
#include "llvm/IR/IRBuilder.h"
|
|
#include "llvm/IR/InstVisitor.h"
|
|
#include "llvm/IR/Instructions.h"
|
|
#include "llvm/Pass.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Support/Debug.h"
|
|
|
|
#define DEBUG_TYPE "oclutil"
|
|
|
|
using namespace llvm;
|
|
using namespace SPIRV;
|
|
|
|
namespace OCLUtil {
|
|
|
|
#ifndef SPIRV_OCL_SPECIAL_TYPES_DEFAULT_ADDR_SPACE
|
|
#define SPIRV_OCL_SPECIAL_TYPES_DEFAULT_ADDR_SPACE SPIRAS_Private
|
|
#endif
|
|
|
|
#ifndef SPIRV_QUEUE_T_ADDR_SPACE
|
|
#define SPIRV_QUEUE_T_ADDR_SPACE SPIRV_OCL_SPECIAL_TYPES_DEFAULT_ADDR_SPACE
|
|
#endif
|
|
|
|
#ifndef SPIRV_EVENT_T_ADDR_SPACE
|
|
#define SPIRV_EVENT_T_ADDR_SPACE SPIRV_OCL_SPECIAL_TYPES_DEFAULT_ADDR_SPACE
|
|
#endif
|
|
|
|
#ifndef SPIRV_AVC_INTEL_T_ADDR_SPACE
|
|
#define SPIRV_AVC_INTEL_T_ADDR_SPACE SPIRV_OCL_SPECIAL_TYPES_DEFAULT_ADDR_SPACE
|
|
#endif
|
|
|
|
#ifndef SPIRV_CLK_EVENT_T_ADDR_SPACE
|
|
#define SPIRV_CLK_EVENT_T_ADDR_SPACE SPIRV_OCL_SPECIAL_TYPES_DEFAULT_ADDR_SPACE
|
|
#endif
|
|
|
|
#ifndef SPIRV_SAMPLER_T_ADDR_SPACE
|
|
#define SPIRV_SAMPLER_T_ADDR_SPACE SPIRAS_Constant
|
|
#endif
|
|
|
|
#ifndef SPIRV_RESERVE_ID_T_ADDR_SPACE
|
|
#define SPIRV_RESERVE_ID_T_ADDR_SPACE SPIRV_OCL_SPECIAL_TYPES_DEFAULT_ADDR_SPACE
|
|
#endif
|
|
// Excerpt from SPIR 2.0 spec.:
|
|
// Pipe objects are represented using pointers to the opaque %opencl.pipe LLVM
|
|
// structure type which reside in the global address space.
|
|
#ifndef SPIRV_PIPE_ADDR_SPACE
|
|
#define SPIRV_PIPE_ADDR_SPACE SPIRAS_Global
|
|
#endif
|
|
// Excerpt from SPIR 2.0 spec.:
|
|
// Note: Images data types reside in global memory and hence should be marked
|
|
// as such in the "kernel arg addr space" metadata.
|
|
#ifndef SPIRV_IMAGE_ADDR_SPACE
|
|
#define SPIRV_IMAGE_ADDR_SPACE SPIRAS_Global
|
|
#endif
|
|
|
|
} // namespace OCLUtil
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Map definitions
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
using namespace OCLUtil;
|
|
namespace SPIRV {
|
|
|
|
template <> void SPIRVMap<OCLMemFenceKind, MemorySemanticsMask>::init() {
|
|
add(OCLMF_Local, MemorySemanticsWorkgroupMemoryMask);
|
|
add(OCLMF_Global, MemorySemanticsCrossWorkgroupMemoryMask);
|
|
add(OCLMF_Image, MemorySemanticsImageMemoryMask);
|
|
}
|
|
|
|
template <>
|
|
void SPIRVMap<OCLMemFenceExtendedKind, MemorySemanticsMask>::init() {
|
|
add(OCLMFEx_Local, MemorySemanticsWorkgroupMemoryMask);
|
|
add(OCLMFEx_Global, MemorySemanticsCrossWorkgroupMemoryMask);
|
|
add(OCLMFEx_Local_Global, MemorySemanticsWorkgroupMemoryMask |
|
|
MemorySemanticsCrossWorkgroupMemoryMask);
|
|
add(OCLMFEx_Image, MemorySemanticsImageMemoryMask);
|
|
add(OCLMFEx_Image_Local,
|
|
MemorySemanticsWorkgroupMemoryMask | MemorySemanticsImageMemoryMask);
|
|
add(OCLMFEx_Image_Global,
|
|
MemorySemanticsCrossWorkgroupMemoryMask | MemorySemanticsImageMemoryMask);
|
|
add(OCLMFEx_Image_Local_Global, MemorySemanticsWorkgroupMemoryMask |
|
|
MemorySemanticsCrossWorkgroupMemoryMask |
|
|
MemorySemanticsImageMemoryMask);
|
|
}
|
|
|
|
template <>
|
|
void SPIRVMap<OCLMemOrderKind, unsigned, MemorySemanticsMask>::init() {
|
|
add(OCLMO_relaxed, MemorySemanticsMaskNone);
|
|
add(OCLMO_acquire, MemorySemanticsAcquireMask);
|
|
add(OCLMO_release, MemorySemanticsReleaseMask);
|
|
add(OCLMO_acq_rel, MemorySemanticsAcquireReleaseMask);
|
|
add(OCLMO_seq_cst, MemorySemanticsSequentiallyConsistentMask);
|
|
}
|
|
|
|
template <> void SPIRVMap<OCLScopeKind, Scope>::init() {
|
|
add(OCLMS_work_item, ScopeInvocation);
|
|
add(OCLMS_work_group, ScopeWorkgroup);
|
|
add(OCLMS_device, ScopeDevice);
|
|
add(OCLMS_all_svm_devices, ScopeCrossDevice);
|
|
add(OCLMS_sub_group, ScopeSubgroup);
|
|
}
|
|
|
|
template <> void SPIRVMap<std::string, Scope>::init() {
|
|
add("work_item", ScopeInvocation);
|
|
add("workgroup", ScopeWorkgroup);
|
|
add("device", ScopeDevice);
|
|
add("all_svm_devices", ScopeCrossDevice);
|
|
add("subgroup", ScopeSubgroup);
|
|
}
|
|
|
|
template <> void SPIRVMap<std::string, SPIRVGroupOperationKind>::init() {
|
|
add("reduce", GroupOperationReduce);
|
|
add("scan_inclusive", GroupOperationInclusiveScan);
|
|
add("scan_exclusive", GroupOperationExclusiveScan);
|
|
add("ballot_bit_count", GroupOperationReduce);
|
|
add("ballot_inclusive_scan", GroupOperationInclusiveScan);
|
|
add("ballot_exclusive_scan", GroupOperationExclusiveScan);
|
|
add("non_uniform_reduce", GroupOperationReduce);
|
|
add("non_uniform_scan_inclusive", GroupOperationInclusiveScan);
|
|
add("non_uniform_scan_exclusive", GroupOperationExclusiveScan);
|
|
add("non_uniform_reduce_logical", GroupOperationReduce);
|
|
add("non_uniform_scan_inclusive_logical", GroupOperationInclusiveScan);
|
|
add("non_uniform_scan_exclusive_logical", GroupOperationExclusiveScan);
|
|
add("clustered_reduce", GroupOperationClusteredReduce);
|
|
}
|
|
|
|
template <> void SPIRVMap<std::string, SPIRVFPRoundingModeKind>::init() {
|
|
add("rte", FPRoundingModeRTE);
|
|
add("rtz", FPRoundingModeRTZ);
|
|
add("rtp", FPRoundingModeRTP);
|
|
add("rtn", FPRoundingModeRTN);
|
|
}
|
|
|
|
template <> void SPIRVMap<OclExt::Kind, std::string>::init() {
|
|
#define _SPIRV_OP(x) add(OclExt::x, #x);
|
|
_SPIRV_OP(cl_images)
|
|
_SPIRV_OP(cl_doubles)
|
|
_SPIRV_OP(cl_khr_int64_base_atomics)
|
|
_SPIRV_OP(cl_khr_int64_extended_atomics)
|
|
_SPIRV_OP(cl_khr_fp16)
|
|
_SPIRV_OP(cl_khr_gl_sharing)
|
|
_SPIRV_OP(cl_khr_gl_event)
|
|
_SPIRV_OP(cl_khr_d3d10_sharing)
|
|
_SPIRV_OP(cl_khr_media_sharing)
|
|
_SPIRV_OP(cl_khr_d3d11_sharing)
|
|
_SPIRV_OP(cl_khr_global_int32_base_atomics)
|
|
_SPIRV_OP(cl_khr_global_int32_extended_atomics)
|
|
_SPIRV_OP(cl_khr_local_int32_base_atomics)
|
|
_SPIRV_OP(cl_khr_local_int32_extended_atomics)
|
|
_SPIRV_OP(cl_khr_byte_addressable_store)
|
|
_SPIRV_OP(cl_khr_3d_image_writes)
|
|
_SPIRV_OP(cl_khr_gl_msaa_sharing)
|
|
_SPIRV_OP(cl_khr_depth_images)
|
|
_SPIRV_OP(cl_khr_gl_depth_images)
|
|
_SPIRV_OP(cl_khr_subgroups)
|
|
_SPIRV_OP(cl_khr_mipmap_image)
|
|
_SPIRV_OP(cl_khr_mipmap_image_writes)
|
|
_SPIRV_OP(cl_khr_egl_event)
|
|
_SPIRV_OP(cl_khr_srgb_image_writes)
|
|
_SPIRV_OP(cl_khr_extended_bit_ops)
|
|
#undef _SPIRV_OP
|
|
}
|
|
|
|
template <> void SPIRVMap<OclExt::Kind, SPIRVCapabilityKind>::init() {
|
|
add(OclExt::cl_images, CapabilityImageBasic);
|
|
add(OclExt::cl_doubles, CapabilityFloat64);
|
|
add(OclExt::cl_khr_int64_base_atomics, CapabilityInt64Atomics);
|
|
add(OclExt::cl_khr_int64_extended_atomics, CapabilityInt64Atomics);
|
|
add(OclExt::cl_khr_fp16, CapabilityFloat16);
|
|
add(OclExt::cl_khr_subgroups, CapabilityGroups);
|
|
add(OclExt::cl_khr_mipmap_image, CapabilityImageMipmap);
|
|
add(OclExt::cl_khr_mipmap_image_writes, CapabilityImageMipmap);
|
|
add(OclExt::cl_khr_extended_bit_ops, CapabilityBitInstructions);
|
|
}
|
|
|
|
/// Map OpenCL work functions to SPIR-V builtin variables.
|
|
template <> void SPIRVMap<std::string, SPIRVBuiltinVariableKind>::init() {
|
|
add("get_work_dim", BuiltInWorkDim);
|
|
add("get_global_size", BuiltInGlobalSize);
|
|
add("get_global_id", BuiltInGlobalInvocationId);
|
|
add("get_global_offset", BuiltInGlobalOffset);
|
|
add("get_local_size", BuiltInWorkgroupSize);
|
|
add("get_enqueued_local_size", BuiltInEnqueuedWorkgroupSize);
|
|
add("get_local_id", BuiltInLocalInvocationId);
|
|
add("get_num_groups", BuiltInNumWorkgroups);
|
|
add("get_group_id", BuiltInWorkgroupId);
|
|
add("get_global_linear_id", BuiltInGlobalLinearId);
|
|
add("get_local_linear_id", BuiltInLocalInvocationIndex);
|
|
// cl_khr_subgroups
|
|
add("get_sub_group_size", BuiltInSubgroupSize);
|
|
add("get_max_sub_group_size", BuiltInSubgroupMaxSize);
|
|
add("get_num_sub_groups", BuiltInNumSubgroups);
|
|
add("get_enqueued_num_sub_groups", BuiltInNumEnqueuedSubgroups);
|
|
add("get_sub_group_id", BuiltInSubgroupId);
|
|
add("get_sub_group_local_id", BuiltInSubgroupLocalInvocationId);
|
|
// cl_khr_subgroup_ballot
|
|
add("get_sub_group_eq_mask", BuiltInSubgroupEqMask);
|
|
add("get_sub_group_ge_mask", BuiltInSubgroupGeMask);
|
|
add("get_sub_group_gt_mask", BuiltInSubgroupGtMask);
|
|
add("get_sub_group_le_mask", BuiltInSubgroupLeMask);
|
|
add("get_sub_group_lt_mask", BuiltInSubgroupLtMask);
|
|
}
|
|
|
|
// Maps uniqued OCL builtin function name to SPIR-V op code.
|
|
// A uniqued OCL builtin function name may be different from the real
|
|
// OCL builtin function name. e.g. instead of atomic_min, atomic_umin
|
|
// is used for atomic_min with unsigned integer parameter.
|
|
// work_group_ and sub_group_ functions are unified as group_ functions
|
|
// except work_group_barrier.
|
|
class SPIRVInstruction;
|
|
template <> void SPIRVMap<std::string, Op, SPIRVInstruction>::init() {
|
|
#define _SPIRV_OP(x, y) add("atom_" #x, OpAtomic##y);
|
|
// cl_khr_int64_base_atomics builtins
|
|
_SPIRV_OP(add, IAdd)
|
|
_SPIRV_OP(sub, ISub)
|
|
_SPIRV_OP(xchg, Exchange)
|
|
_SPIRV_OP(dec, IDecrement)
|
|
_SPIRV_OP(inc, IIncrement)
|
|
_SPIRV_OP(cmpxchg, CompareExchange)
|
|
// cl_khr_int64_extended_atomics builtins
|
|
_SPIRV_OP(min, SMin)
|
|
_SPIRV_OP(max, SMax)
|
|
_SPIRV_OP(and, And)
|
|
_SPIRV_OP(or, Or)
|
|
_SPIRV_OP(xor, Xor)
|
|
#undef _SPIRV_OP
|
|
#define _SPIRV_OP(x, y) add("atomic_" #x, Op##y);
|
|
// CL 2.0 atomic builtins
|
|
_SPIRV_OP(flag_test_and_set_explicit, AtomicFlagTestAndSet)
|
|
_SPIRV_OP(flag_clear_explicit, AtomicFlagClear)
|
|
_SPIRV_OP(load_explicit, AtomicLoad)
|
|
_SPIRV_OP(store_explicit, AtomicStore)
|
|
_SPIRV_OP(exchange_explicit, AtomicExchange)
|
|
_SPIRV_OP(compare_exchange_strong_explicit, AtomicCompareExchange)
|
|
_SPIRV_OP(compare_exchange_weak_explicit, AtomicCompareExchangeWeak)
|
|
_SPIRV_OP(inc, AtomicIIncrement)
|
|
_SPIRV_OP(dec, AtomicIDecrement)
|
|
_SPIRV_OP(fetch_add_explicit, AtomicIAdd)
|
|
_SPIRV_OP(fetch_sub_explicit, AtomicISub)
|
|
_SPIRV_OP(fetch_umin_explicit, AtomicUMin)
|
|
_SPIRV_OP(fetch_umax_explicit, AtomicUMax)
|
|
_SPIRV_OP(fetch_min_explicit, AtomicSMin)
|
|
_SPIRV_OP(fetch_max_explicit, AtomicSMax)
|
|
_SPIRV_OP(fetch_and_explicit, AtomicAnd)
|
|
_SPIRV_OP(fetch_or_explicit, AtomicOr)
|
|
_SPIRV_OP(fetch_xor_explicit, AtomicXor)
|
|
#undef _SPIRV_OP
|
|
#define _SPIRV_OP(x, y) add(#x, Op##y);
|
|
_SPIRV_OP(dot, Dot)
|
|
_SPIRV_OP(async_work_group_copy, GroupAsyncCopy)
|
|
_SPIRV_OP(async_work_group_strided_copy, GroupAsyncCopy)
|
|
_SPIRV_OP(wait_group_events, GroupWaitEvents)
|
|
_SPIRV_OP(isequal, FOrdEqual)
|
|
_SPIRV_OP(isnotequal, FUnordNotEqual)
|
|
_SPIRV_OP(isgreater, FOrdGreaterThan)
|
|
_SPIRV_OP(isgreaterequal, FOrdGreaterThanEqual)
|
|
_SPIRV_OP(isless, FOrdLessThan)
|
|
_SPIRV_OP(islessequal, FOrdLessThanEqual)
|
|
_SPIRV_OP(islessgreater, FOrdNotEqual)
|
|
_SPIRV_OP(isordered, Ordered)
|
|
_SPIRV_OP(isunordered, Unordered)
|
|
_SPIRV_OP(isfinite, IsFinite)
|
|
_SPIRV_OP(isinf, IsInf)
|
|
_SPIRV_OP(isnan, IsNan)
|
|
_SPIRV_OP(isnormal, IsNormal)
|
|
_SPIRV_OP(signbit, SignBitSet)
|
|
_SPIRV_OP(any, Any)
|
|
_SPIRV_OP(all, All)
|
|
_SPIRV_OP(popcount, BitCount)
|
|
_SPIRV_OP(get_fence, GenericPtrMemSemantics)
|
|
// CL 2.0 kernel enqueue builtins
|
|
_SPIRV_OP(enqueue_marker, EnqueueMarker)
|
|
_SPIRV_OP(enqueue_kernel, EnqueueKernel)
|
|
_SPIRV_OP(get_kernel_sub_group_count_for_ndrange_impl,
|
|
GetKernelNDrangeSubGroupCount)
|
|
_SPIRV_OP(get_kernel_max_sub_group_size_for_ndrange_impl,
|
|
GetKernelNDrangeMaxSubGroupSize)
|
|
_SPIRV_OP(get_kernel_work_group_size_impl, GetKernelWorkGroupSize)
|
|
_SPIRV_OP(get_kernel_preferred_work_group_size_multiple_impl,
|
|
GetKernelPreferredWorkGroupSizeMultiple)
|
|
_SPIRV_OP(retain_event, RetainEvent)
|
|
_SPIRV_OP(release_event, ReleaseEvent)
|
|
_SPIRV_OP(create_user_event, CreateUserEvent)
|
|
_SPIRV_OP(is_valid_event, IsValidEvent)
|
|
_SPIRV_OP(set_user_event_status, SetUserEventStatus)
|
|
_SPIRV_OP(capture_event_profiling_info, CaptureEventProfilingInfo)
|
|
_SPIRV_OP(get_default_queue, GetDefaultQueue)
|
|
_SPIRV_OP(ndrange_1D, BuildNDRange)
|
|
_SPIRV_OP(ndrange_2D, BuildNDRange)
|
|
_SPIRV_OP(ndrange_3D, BuildNDRange)
|
|
// Generic Address Space Casts
|
|
_SPIRV_OP(to_global, GenericCastToPtrExplicit)
|
|
_SPIRV_OP(to_local, GenericCastToPtrExplicit)
|
|
_SPIRV_OP(to_private, GenericCastToPtrExplicit)
|
|
// CL 2.0 pipe builtins
|
|
_SPIRV_OP(read_pipe_2, ReadPipe)
|
|
_SPIRV_OP(write_pipe_2, WritePipe)
|
|
_SPIRV_OP(read_pipe_2_bl, ReadPipeBlockingINTEL)
|
|
_SPIRV_OP(write_pipe_2_bl, WritePipeBlockingINTEL)
|
|
_SPIRV_OP(read_pipe_4, ReservedReadPipe)
|
|
_SPIRV_OP(write_pipe_4, ReservedWritePipe)
|
|
_SPIRV_OP(reserve_read_pipe, ReserveReadPipePackets)
|
|
_SPIRV_OP(reserve_write_pipe, ReserveWritePipePackets)
|
|
_SPIRV_OP(commit_read_pipe, CommitReadPipe)
|
|
_SPIRV_OP(commit_write_pipe, CommitWritePipe)
|
|
_SPIRV_OP(is_valid_reserve_id, IsValidReserveId)
|
|
_SPIRV_OP(group_reserve_read_pipe, GroupReserveReadPipePackets)
|
|
_SPIRV_OP(group_reserve_write_pipe, GroupReserveWritePipePackets)
|
|
_SPIRV_OP(group_commit_read_pipe, GroupCommitReadPipe)
|
|
_SPIRV_OP(group_commit_write_pipe, GroupCommitWritePipe)
|
|
_SPIRV_OP(get_pipe_num_packets_ro, GetNumPipePackets)
|
|
_SPIRV_OP(get_pipe_num_packets_wo, GetNumPipePackets)
|
|
_SPIRV_OP(get_pipe_max_packets_ro, GetMaxPipePackets)
|
|
_SPIRV_OP(get_pipe_max_packets_wo, GetMaxPipePackets)
|
|
// CL 2.0 workgroup builtins
|
|
_SPIRV_OP(group_all, GroupAll)
|
|
_SPIRV_OP(group_any, GroupAny)
|
|
_SPIRV_OP(group_broadcast, GroupBroadcast)
|
|
_SPIRV_OP(group_iadd, GroupIAdd)
|
|
_SPIRV_OP(group_fadd, GroupFAdd)
|
|
_SPIRV_OP(group_fmin, GroupFMin)
|
|
_SPIRV_OP(group_umin, GroupUMin)
|
|
_SPIRV_OP(group_smin, GroupSMin)
|
|
_SPIRV_OP(group_fmax, GroupFMax)
|
|
_SPIRV_OP(group_umax, GroupUMax)
|
|
_SPIRV_OP(group_smax, GroupSMax)
|
|
_SPIRV_OP(group_imul, GroupIMulKHR)
|
|
_SPIRV_OP(group_fmul, GroupFMulKHR)
|
|
_SPIRV_OP(group_ibitwise_and, GroupBitwiseAndKHR)
|
|
_SPIRV_OP(group_ibitwise_or, GroupBitwiseOrKHR)
|
|
_SPIRV_OP(group_ibitwise_xor, GroupBitwiseXorKHR)
|
|
_SPIRV_OP(group_ilogical_and, GroupLogicalAndKHR)
|
|
_SPIRV_OP(group_ilogical_or, GroupLogicalOrKHR)
|
|
_SPIRV_OP(group_ilogical_xor, GroupLogicalXorKHR)
|
|
// CL image builtins
|
|
_SPIRV_OP(SampledImage, SampledImage)
|
|
_SPIRV_OP(ImageSampleExplicitLod, ImageSampleExplicitLod)
|
|
_SPIRV_OP(read_image, ImageRead)
|
|
_SPIRV_OP(write_image, ImageWrite)
|
|
_SPIRV_OP(get_image_channel_data_type, ImageQueryFormat)
|
|
_SPIRV_OP(get_image_channel_order, ImageQueryOrder)
|
|
_SPIRV_OP(get_image_num_mip_levels, ImageQueryLevels)
|
|
_SPIRV_OP(get_image_num_samples, ImageQuerySamples)
|
|
// Intel Subgroups builtins
|
|
_SPIRV_OP(intel_sub_group_shuffle, SubgroupShuffleINTEL)
|
|
_SPIRV_OP(intel_sub_group_shuffle_down, SubgroupShuffleDownINTEL)
|
|
_SPIRV_OP(intel_sub_group_shuffle_up, SubgroupShuffleUpINTEL)
|
|
_SPIRV_OP(intel_sub_group_shuffle_xor, SubgroupShuffleXorINTEL)
|
|
// Intel media_block_io builtins
|
|
_SPIRV_OP(intel_sub_group_media_block_read, SubgroupImageMediaBlockReadINTEL)
|
|
_SPIRV_OP(intel_sub_group_media_block_write,
|
|
SubgroupImageMediaBlockWriteINTEL)
|
|
// cl_khr_subgroup_non_uniform_vote
|
|
_SPIRV_OP(group_elect, GroupNonUniformElect)
|
|
_SPIRV_OP(group_non_uniform_all, GroupNonUniformAll)
|
|
_SPIRV_OP(group_non_uniform_any, GroupNonUniformAny)
|
|
_SPIRV_OP(group_non_uniform_all_equal, GroupNonUniformAllEqual)
|
|
// cl_khr_subgroup_ballot
|
|
_SPIRV_OP(group_non_uniform_broadcast, GroupNonUniformBroadcast)
|
|
_SPIRV_OP(group_broadcast_first, GroupNonUniformBroadcastFirst)
|
|
_SPIRV_OP(group_ballot, GroupNonUniformBallot)
|
|
_SPIRV_OP(group_inverse_ballot, GroupNonUniformInverseBallot)
|
|
_SPIRV_OP(group_ballot_bit_extract, GroupNonUniformBallotBitExtract)
|
|
_SPIRV_OP(group_ballot_bit_count_iadd, GroupNonUniformBallotBitCount)
|
|
_SPIRV_OP(group_ballot_find_lsb, GroupNonUniformBallotFindLSB)
|
|
_SPIRV_OP(group_ballot_find_msb, GroupNonUniformBallotFindMSB)
|
|
// cl_khr_subgroup_non_uniform_arithmetic
|
|
_SPIRV_OP(group_non_uniform_iadd, GroupNonUniformIAdd)
|
|
_SPIRV_OP(group_non_uniform_fadd, GroupNonUniformFAdd)
|
|
_SPIRV_OP(group_non_uniform_imul, GroupNonUniformIMul)
|
|
_SPIRV_OP(group_non_uniform_fmul, GroupNonUniformFMul)
|
|
_SPIRV_OP(group_non_uniform_smin, GroupNonUniformSMin)
|
|
_SPIRV_OP(group_non_uniform_umin, GroupNonUniformUMin)
|
|
_SPIRV_OP(group_non_uniform_fmin, GroupNonUniformFMin)
|
|
_SPIRV_OP(group_non_uniform_smax, GroupNonUniformSMax)
|
|
_SPIRV_OP(group_non_uniform_umax, GroupNonUniformUMax)
|
|
_SPIRV_OP(group_non_uniform_fmax, GroupNonUniformFMax)
|
|
_SPIRV_OP(group_non_uniform_iand, GroupNonUniformBitwiseAnd)
|
|
_SPIRV_OP(group_non_uniform_ior, GroupNonUniformBitwiseOr)
|
|
_SPIRV_OP(group_non_uniform_ixor, GroupNonUniformBitwiseXor)
|
|
_SPIRV_OP(group_non_uniform_logical_iand, GroupNonUniformLogicalAnd)
|
|
_SPIRV_OP(group_non_uniform_logical_ior, GroupNonUniformLogicalOr)
|
|
_SPIRV_OP(group_non_uniform_logical_ixor, GroupNonUniformLogicalXor)
|
|
// cl_khr_subgroup_shuffle
|
|
_SPIRV_OP(group_shuffle, GroupNonUniformShuffle)
|
|
_SPIRV_OP(group_shuffle_xor, GroupNonUniformShuffleXor)
|
|
// cl_khr_subgroup_shuffle_relative
|
|
_SPIRV_OP(group_shuffle_up, GroupNonUniformShuffleUp)
|
|
_SPIRV_OP(group_shuffle_down, GroupNonUniformShuffleDown)
|
|
// cl_khr_subgroup_rotate
|
|
_SPIRV_OP(group_rotate, GroupNonUniformRotateKHR)
|
|
_SPIRV_OP(group_clustered_rotate, GroupNonUniformRotateKHR)
|
|
// cl_khr_extended_bit_ops
|
|
_SPIRV_OP(bitfield_insert, BitFieldInsert)
|
|
_SPIRV_OP(bitfield_extract_signed, BitFieldSExtract)
|
|
_SPIRV_OP(bitfield_extract_unsigned, BitFieldUExtract)
|
|
_SPIRV_OP(bit_reverse, BitReverse)
|
|
// cl_khr_split_work_group_barrier
|
|
_SPIRV_OP(intel_work_group_barrier_arrive, ControlBarrierArriveINTEL)
|
|
_SPIRV_OP(intel_work_group_barrier_wait, ControlBarrierWaitINTEL)
|
|
#undef _SPIRV_OP
|
|
}
|
|
|
|
template <> void SPIRVMap<std::string, Op, OCL12Builtin>::init() {
|
|
#define _SPIRV_OP(x, y) add(#x, Op##y);
|
|
_SPIRV_OP(add, AtomicIAdd)
|
|
_SPIRV_OP(sub, AtomicISub)
|
|
_SPIRV_OP(xchg, AtomicExchange)
|
|
_SPIRV_OP(cmpxchg, AtomicCompareExchange)
|
|
_SPIRV_OP(inc, AtomicIIncrement)
|
|
_SPIRV_OP(dec, AtomicIDecrement)
|
|
_SPIRV_OP(min, AtomicSMin)
|
|
_SPIRV_OP(max, AtomicSMax)
|
|
_SPIRV_OP(umin, AtomicUMin)
|
|
_SPIRV_OP(umax, AtomicUMax)
|
|
_SPIRV_OP(and, AtomicAnd)
|
|
_SPIRV_OP(or, AtomicOr)
|
|
_SPIRV_OP(xor, AtomicXor)
|
|
#undef _SPIRV_OP
|
|
}
|
|
|
|
// SPV_INTEL_device_side_avc_motion_estimation extension builtins
|
|
class SPIRVSubgroupsAVCIntelInst;
|
|
template <> void SPIRVMap<std::string, Op, SPIRVSubgroupsAVCIntelInst>::init() {
|
|
// Here is a workaround for a bug in the specification:
|
|
// 'avc' missed in 'intel_sub_group_avc' prefix.
|
|
add("intel_sub_group_ime_ref_window_size",
|
|
OpSubgroupAvcImeRefWindowSizeINTEL);
|
|
|
|
#define _SPIRV_OP(x, y) add("intel_sub_group_avc_" #x, OpSubgroupAvc##y##INTEL);
|
|
// Initialization phase functions
|
|
_SPIRV_OP(ime_initialize, ImeInitialize)
|
|
_SPIRV_OP(fme_initialize, FmeInitialize)
|
|
_SPIRV_OP(bme_initialize, BmeInitialize)
|
|
_SPIRV_OP(sic_initialize, SicInitialize)
|
|
|
|
// Result and payload types conversion functions
|
|
_SPIRV_OP(mce_convert_to_ime_payload, MceConvertToImePayload)
|
|
_SPIRV_OP(mce_convert_to_ime_result, MceConvertToImeResult)
|
|
_SPIRV_OP(mce_convert_to_ref_payload, MceConvertToRefPayload)
|
|
_SPIRV_OP(mce_convert_to_ref_result, MceConvertToRefResult)
|
|
_SPIRV_OP(mce_convert_to_sic_payload, MceConvertToSicPayload)
|
|
_SPIRV_OP(mce_convert_to_sic_result, MceConvertToSicResult)
|
|
_SPIRV_OP(ime_convert_to_mce_payload, ImeConvertToMcePayload)
|
|
_SPIRV_OP(ime_convert_to_mce_result, ImeConvertToMceResult)
|
|
_SPIRV_OP(ref_convert_to_mce_payload, RefConvertToMcePayload)
|
|
_SPIRV_OP(ref_convert_to_mce_result, RefConvertToMceResult)
|
|
_SPIRV_OP(sic_convert_to_mce_payload, SicConvertToMcePayload)
|
|
_SPIRV_OP(sic_convert_to_mce_result, SicConvertToMceResult)
|
|
#undef _SPIRV_OP
|
|
|
|
// MCE instructions
|
|
#define _SPIRV_OP(x, y) \
|
|
add("intel_sub_group_avc_mce_" #x, OpSubgroupAvcMce##y##INTEL);
|
|
_SPIRV_OP(get_default_inter_base_multi_reference_penalty,
|
|
GetDefaultInterBaseMultiReferencePenalty)
|
|
_SPIRV_OP(set_inter_base_multi_reference_penalty,
|
|
SetInterBaseMultiReferencePenalty)
|
|
_SPIRV_OP(get_default_inter_shape_penalty, GetDefaultInterShapePenalty)
|
|
_SPIRV_OP(set_inter_shape_penalty, SetInterShapePenalty)
|
|
_SPIRV_OP(get_default_inter_direction_penalty,
|
|
GetDefaultInterDirectionPenalty)
|
|
_SPIRV_OP(set_inter_direction_penalty, SetInterDirectionPenalty)
|
|
_SPIRV_OP(get_default_intra_luma_shape_penalty,
|
|
GetDefaultIntraLumaShapePenalty)
|
|
_SPIRV_OP(get_default_inter_motion_vector_cost_table,
|
|
GetDefaultInterMotionVectorCostTable)
|
|
_SPIRV_OP(get_default_high_penalty_cost_table, GetDefaultHighPenaltyCostTable)
|
|
_SPIRV_OP(get_default_medium_penalty_cost_table,
|
|
GetDefaultMediumPenaltyCostTable)
|
|
_SPIRV_OP(get_default_low_penalty_cost_table, GetDefaultLowPenaltyCostTable)
|
|
_SPIRV_OP(set_motion_vector_cost_function, SetMotionVectorCostFunction)
|
|
_SPIRV_OP(get_default_intra_luma_mode_penalty, GetDefaultIntraLumaModePenalty)
|
|
_SPIRV_OP(get_default_non_dc_luma_intra_penalty,
|
|
GetDefaultNonDcLumaIntraPenalty)
|
|
_SPIRV_OP(get_default_intra_chroma_mode_base_penalty,
|
|
GetDefaultIntraChromaModeBasePenalty)
|
|
_SPIRV_OP(set_ac_only_haar, SetAcOnlyHaar)
|
|
_SPIRV_OP(set_source_interlaced_field_polarity,
|
|
SetSourceInterlacedFieldPolarity)
|
|
_SPIRV_OP(set_single_reference_interlaced_field_polarity,
|
|
SetSingleReferenceInterlacedFieldPolarity)
|
|
_SPIRV_OP(set_dual_reference_interlaced_field_polarities,
|
|
SetDualReferenceInterlacedFieldPolarities)
|
|
_SPIRV_OP(get_motion_vectors, GetMotionVectors)
|
|
_SPIRV_OP(get_inter_distortions, GetInterDistortions)
|
|
_SPIRV_OP(get_best_inter_distortion, GetBestInterDistortions)
|
|
_SPIRV_OP(get_inter_major_shape, GetInterMajorShape)
|
|
_SPIRV_OP(get_inter_minor_shapes, GetInterMinorShape)
|
|
_SPIRV_OP(get_inter_directions, GetInterDirections)
|
|
_SPIRV_OP(get_inter_motion_vector_count, GetInterMotionVectorCount)
|
|
_SPIRV_OP(get_inter_reference_ids, GetInterReferenceIds)
|
|
_SPIRV_OP(get_inter_reference_interlaced_field_polarities,
|
|
GetInterReferenceInterlacedFieldPolarities)
|
|
#undef _SPIRV_OP
|
|
|
|
// IME instructions
|
|
#define _SPIRV_OP(x, y) \
|
|
add("intel_sub_group_avc_ime_" #x, OpSubgroupAvcIme##y##INTEL);
|
|
_SPIRV_OP(set_single_reference, SetSingleReference)
|
|
_SPIRV_OP(set_dual_reference, SetDualReference)
|
|
_SPIRV_OP(ref_window_size, RefWindowSize)
|
|
_SPIRV_OP(adjust_ref_offset, AdjustRefOffset)
|
|
_SPIRV_OP(set_max_motion_vector_count, SetMaxMotionVectorCount)
|
|
_SPIRV_OP(set_unidirectional_mix_disable, SetUnidirectionalMixDisable)
|
|
_SPIRV_OP(set_early_search_termination_threshold,
|
|
SetEarlySearchTerminationThreshold)
|
|
_SPIRV_OP(set_weighted_sad, SetWeightedSad)
|
|
_SPIRV_OP(evaluate_with_single_reference, EvaluateWithSingleReference)
|
|
_SPIRV_OP(evaluate_with_dual_reference, EvaluateWithDualReference)
|
|
_SPIRV_OP(evaluate_with_single_reference_streamin,
|
|
EvaluateWithSingleReferenceStreamin)
|
|
_SPIRV_OP(evaluate_with_dual_reference_streamin,
|
|
EvaluateWithDualReferenceStreamin)
|
|
_SPIRV_OP(evaluate_with_single_reference_streamout,
|
|
EvaluateWithSingleReferenceStreamout)
|
|
_SPIRV_OP(evaluate_with_dual_reference_streamout,
|
|
EvaluateWithDualReferenceStreamout)
|
|
_SPIRV_OP(evaluate_with_single_reference_streaminout,
|
|
EvaluateWithSingleReferenceStreaminout)
|
|
_SPIRV_OP(evaluate_with_dual_reference_streaminout,
|
|
EvaluateWithDualReferenceStreaminout)
|
|
_SPIRV_OP(get_single_reference_streamin, GetSingleReferenceStreamin)
|
|
_SPIRV_OP(get_dual_reference_streamin, GetDualReferenceStreamin)
|
|
_SPIRV_OP(strip_single_reference_streamout, StripSingleReferenceStreamout)
|
|
_SPIRV_OP(strip_dual_reference_streamout, StripDualReferenceStreamout)
|
|
_SPIRV_OP(get_border_reached, GetBorderReached)
|
|
_SPIRV_OP(get_truncated_search_indication, GetTruncatedSearchIndication)
|
|
_SPIRV_OP(get_unidirectional_early_search_termination,
|
|
GetUnidirectionalEarlySearchTermination)
|
|
_SPIRV_OP(get_weighting_pattern_minimum_motion_vector,
|
|
GetWeightingPatternMinimumMotionVector)
|
|
_SPIRV_OP(get_weighting_pattern_minimum_distortion,
|
|
GetWeightingPatternMinimumDistortion)
|
|
#undef _SPIRV_OP
|
|
|
|
#define _SPIRV_OP(x, y) \
|
|
add("intel_sub_group_avc_ime_get_streamout_major_shape_" #x, \
|
|
OpSubgroupAvcImeGetStreamout##y##INTEL);
|
|
_SPIRV_OP(motion_vectors_single_reference,
|
|
SingleReferenceMajorShapeMotionVectors)
|
|
_SPIRV_OP(distortions_single_reference, SingleReferenceMajorShapeDistortions)
|
|
_SPIRV_OP(reference_ids_single_reference,
|
|
SingleReferenceMajorShapeReferenceIds)
|
|
_SPIRV_OP(motion_vectors_dual_reference, DualReferenceMajorShapeMotionVectors)
|
|
_SPIRV_OP(distortions_dual_reference, DualReferenceMajorShapeDistortions)
|
|
_SPIRV_OP(reference_ids_dual_reference, DualReferenceMajorShapeReferenceIds)
|
|
#undef _SPIRV_OP
|
|
|
|
// REF instructions
|
|
#define _SPIRV_OP(x, y) \
|
|
add("intel_sub_group_avc_ref_" #x, OpSubgroupAvcRef##y##INTEL);
|
|
_SPIRV_OP(set_bidirectional_mix_disable, SetBidirectionalMixDisable)
|
|
_SPIRV_OP(set_bilinear_filter_enable, SetBilinearFilterEnable)
|
|
_SPIRV_OP(evaluate_with_single_reference, EvaluateWithSingleReference)
|
|
_SPIRV_OP(evaluate_with_dual_reference, EvaluateWithDualReference)
|
|
_SPIRV_OP(evaluate_with_multi_reference, EvaluateWithMultiReference)
|
|
_SPIRV_OP(evaluate_with_multi_reference_interlaced,
|
|
EvaluateWithMultiReferenceInterlaced)
|
|
#undef _SPIRV_OP
|
|
|
|
// SIC instructions
|
|
#define _SPIRV_OP(x, y) \
|
|
add("intel_sub_group_avc_sic_" #x, OpSubgroupAvcSic##y##INTEL);
|
|
_SPIRV_OP(configure_skc, ConfigureSkc)
|
|
_SPIRV_OP(configure_ipe_luma, ConfigureIpeLuma)
|
|
_SPIRV_OP(configure_ipe_luma_chroma, ConfigureIpeLumaChroma)
|
|
_SPIRV_OP(get_motion_vector_mask, GetMotionVectorMask)
|
|
_SPIRV_OP(set_intra_luma_shape_penalty, SetIntraLumaShapePenalty)
|
|
_SPIRV_OP(set_intra_luma_mode_cost_function, SetIntraLumaModeCostFunction)
|
|
_SPIRV_OP(set_intra_chroma_mode_cost_function, SetIntraChromaModeCostFunction)
|
|
_SPIRV_OP(set_skc_bilinear_filter_enable, SetBilinearFilterEnable)
|
|
_SPIRV_OP(set_skc_forward_transform_enable, SetSkcForwardTransformEnable)
|
|
_SPIRV_OP(set_block_based_raw_skip_sad, SetBlockBasedRawSkipSad)
|
|
_SPIRV_OP(evaluate_ipe, EvaluateIpe)
|
|
_SPIRV_OP(evaluate_with_single_reference, EvaluateWithSingleReference)
|
|
_SPIRV_OP(evaluate_with_dual_reference, EvaluateWithDualReference)
|
|
_SPIRV_OP(evaluate_with_multi_reference, EvaluateWithMultiReference)
|
|
_SPIRV_OP(evaluate_with_multi_reference_interlaced,
|
|
EvaluateWithMultiReferenceInterlaced)
|
|
_SPIRV_OP(get_ipe_luma_shape, GetIpeLumaShape)
|
|
_SPIRV_OP(get_best_ipe_luma_distortion, GetBestIpeLumaDistortion)
|
|
_SPIRV_OP(get_best_ipe_chroma_distortion, GetBestIpeChromaDistortion)
|
|
_SPIRV_OP(get_packed_ipe_luma_modes, GetPackedIpeLumaModes)
|
|
_SPIRV_OP(get_ipe_chroma_mode, GetIpeChromaMode)
|
|
_SPIRV_OP(get_packed_skc_luma_count_threshold, GetPackedSkcLumaCountThreshold)
|
|
_SPIRV_OP(get_packed_skc_luma_sum_threshold, GetPackedSkcLumaSumThreshold)
|
|
_SPIRV_OP(get_inter_raw_sads, GetInterRawSads)
|
|
#undef _SPIRV_OP
|
|
}
|
|
|
|
template <> void SPIRVMap<std::string, Op, OCLOpaqueType>::init() {
|
|
add("opencl.event_t", OpTypeEvent);
|
|
add("opencl.pipe_t", OpTypePipe);
|
|
add("opencl.clk_event_t", OpTypeDeviceEvent);
|
|
add("opencl.reserve_id_t", OpTypeReserveId);
|
|
add("opencl.queue_t", OpTypeQueue);
|
|
add("opencl.sampler_t", OpTypeSampler);
|
|
}
|
|
|
|
template <> void LLVMSPIRVAtomicRmwOpCodeMap::init() {
|
|
add(llvm::AtomicRMWInst::Xchg, OpAtomicExchange);
|
|
add(llvm::AtomicRMWInst::Add, OpAtomicIAdd);
|
|
add(llvm::AtomicRMWInst::Sub, OpAtomicISub);
|
|
add(llvm::AtomicRMWInst::And, OpAtomicAnd);
|
|
add(llvm::AtomicRMWInst::Or, OpAtomicOr);
|
|
add(llvm::AtomicRMWInst::Xor, OpAtomicXor);
|
|
add(llvm::AtomicRMWInst::Max, OpAtomicSMax);
|
|
add(llvm::AtomicRMWInst::Min, OpAtomicSMin);
|
|
add(llvm::AtomicRMWInst::UMax, OpAtomicUMax);
|
|
add(llvm::AtomicRMWInst::UMin, OpAtomicUMin);
|
|
add(llvm::AtomicRMWInst::FAdd, OpAtomicFAddEXT);
|
|
add(llvm::AtomicRMWInst::FMin, OpAtomicFMinEXT);
|
|
add(llvm::AtomicRMWInst::FMax, OpAtomicFMaxEXT);
|
|
}
|
|
|
|
} // namespace SPIRV
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Functions for getting builtin call info
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
namespace OCLUtil {
|
|
|
|
AtomicWorkItemFenceLiterals getAtomicWorkItemFenceLiterals(CallInst *CI) {
|
|
return std::make_tuple(getArgAsInt(CI, 0),
|
|
static_cast<OCLMemOrderKind>(getArgAsInt(CI, 1)),
|
|
static_cast<OCLScopeKind>(getArgAsInt(CI, 2)));
|
|
}
|
|
|
|
size_t getAtomicBuiltinNumMemoryOrderArgs(StringRef Name) {
|
|
if (Name.starts_with("atomic_compare_exchange"))
|
|
return 2;
|
|
return 1;
|
|
}
|
|
|
|
size_t getSPIRVAtomicBuiltinNumMemoryOrderArgs(Op OC) {
|
|
if (OC == OpAtomicCompareExchange || OC == OpAtomicCompareExchangeWeak)
|
|
return 2;
|
|
return 1;
|
|
}
|
|
|
|
// atomic_fetch_[add, sub, min, max] and atomic_fetch_[add, sub, min,
|
|
// max]_explicit functions declared in clang headers should be translated
|
|
// to corresponding FP-typed Atomic Instructions
|
|
bool isComputeAtomicOCLBuiltin(StringRef DemangledName) {
|
|
if (!DemangledName.starts_with(kOCLBuiltinName::AtomicPrefix) &&
|
|
!DemangledName.starts_with(kOCLBuiltinName::AtomPrefix))
|
|
return false;
|
|
|
|
return llvm::StringSwitch<bool>(DemangledName)
|
|
.EndsWith("atomic_add", true)
|
|
.EndsWith("atomic_sub", true)
|
|
.EndsWith("atomic_min", true)
|
|
.EndsWith("atomic_max", true)
|
|
.EndsWith("atom_add", true)
|
|
.EndsWith("atom_sub", true)
|
|
.EndsWith("atom_min", true)
|
|
.EndsWith("atom_max", true)
|
|
.EndsWith("inc", true)
|
|
.EndsWith("dec", true)
|
|
.EndsWith("cmpxchg", true)
|
|
.EndsWith("and", true)
|
|
.EndsWith("or", true)
|
|
.EndsWith("xor", true)
|
|
.EndsWith("or_explicit", true)
|
|
.EndsWith("xor_explicit", true)
|
|
.EndsWith("and_explicit", true)
|
|
.Default(false);
|
|
}
|
|
|
|
BarrierLiterals getBarrierLiterals(CallInst *CI) {
|
|
auto N = CI->arg_size();
|
|
assert(N == 1 || N == 2);
|
|
|
|
StringRef DemangledName;
|
|
assert(CI->getCalledFunction() && "Unexpected indirect call");
|
|
if (!oclIsBuiltin(CI->getCalledFunction()->getName(), DemangledName)) {
|
|
assert(0 &&
|
|
"call must a builtin (work_group_barrier or sub_group_barrier)");
|
|
}
|
|
|
|
OCLScopeKind Scope = OCLMS_work_group;
|
|
if (DemangledName == kOCLBuiltinName::SubGroupBarrier) {
|
|
Scope = OCLMS_sub_group;
|
|
}
|
|
|
|
return std::make_tuple(getArgAsInt(CI, 0),
|
|
N == 1 ? OCLMS_work_group
|
|
: static_cast<OCLScopeKind>(getArgAsInt(CI, 1)),
|
|
Scope);
|
|
}
|
|
|
|
unsigned getExtOp(StringRef OrigName, StringRef GivenDemangledName) {
|
|
std::string DemangledName{GivenDemangledName};
|
|
if (DemangledName.empty() || !oclIsBuiltin(OrigName, GivenDemangledName))
|
|
return ~0U;
|
|
LLVM_DEBUG(dbgs() << "getExtOp: demangled name: " << DemangledName << '\n');
|
|
OCLExtOpKind EOC;
|
|
bool Found = OCLExtOpMap::rfind(DemangledName, &EOC);
|
|
if (!Found) {
|
|
std::string Prefix;
|
|
switch (lastFuncParamType(OrigName)) {
|
|
case ParamType::UNSIGNED:
|
|
Prefix = "u_";
|
|
break;
|
|
case ParamType::SIGNED:
|
|
Prefix = "s_";
|
|
break;
|
|
case ParamType::FLOAT:
|
|
Prefix = "f";
|
|
break;
|
|
case ParamType::UNKNOWN:
|
|
break;
|
|
}
|
|
Found = OCLExtOpMap::rfind(Prefix + DemangledName, &EOC);
|
|
}
|
|
if (Found)
|
|
return EOC;
|
|
else
|
|
return ~0U;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Functions for getting module info
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
unsigned encodeOCLVer(unsigned short Major, unsigned char Minor,
|
|
unsigned char Rev) {
|
|
return (Major * 100 + Minor) * 1000 + Rev;
|
|
}
|
|
|
|
std::tuple<unsigned short, unsigned char, unsigned char>
|
|
decodeOCLVer(unsigned Ver) {
|
|
unsigned short Major = Ver / 100000;
|
|
unsigned char Minor = (Ver % 100000) / 1000;
|
|
unsigned char Rev = Ver % 1000;
|
|
return std::make_tuple(Major, Minor, Rev);
|
|
}
|
|
|
|
unsigned getOCLVersion(Module *M, bool AllowMulti) {
|
|
NamedMDNode *NamedMDOCLVer = M->getNamedMetadata(kSPIR2MD::OCLVer);
|
|
NamedMDNode *NamedMDOCLCXXVer = M->getNamedMetadata(kSPIR2MD::OCLCXXVer);
|
|
if (!NamedMDOCLVer && !NamedMDOCLCXXVer)
|
|
return 0;
|
|
|
|
// If the module was linked with another module, there may be multiple
|
|
// operands.
|
|
auto GetVerPair = [](unsigned I, NamedMDNode *NamedMD) {
|
|
auto *MD = NamedMD->getOperand(I);
|
|
return std::make_pair(getMDOperandAsInt(MD, 0), getMDOperandAsInt(MD, 1));
|
|
};
|
|
auto GetVer = [=](NamedMDNode *NamedMD) {
|
|
assert(NamedMD->getNumOperands() && "Invalid SPIR");
|
|
if (!AllowMulti && NamedMD->getNumOperands() != 1)
|
|
report_fatal_error(
|
|
llvm::Twine("Multiple OCL version metadata not allowed"));
|
|
auto Ver = GetVerPair(0, NamedMD);
|
|
for (unsigned I = 1, E = NamedMD->getNumOperands(); I != E; ++I)
|
|
if (Ver != GetVerPair(I, NamedMD))
|
|
report_fatal_error(llvm::Twine("OCL version mismatch"));
|
|
return encodeOCLVer(Ver.first, Ver.second, 0);
|
|
};
|
|
unsigned OCLVer = NamedMDOCLVer ? GetVer(NamedMDOCLVer) : 0;
|
|
unsigned OCLCXXVer = NamedMDOCLCXXVer ? GetVer(NamedMDOCLCXXVer) : 0;
|
|
// Check if OCLCXXVer is compatible with OCLVer
|
|
if (OCLVer && OCLCXXVer) {
|
|
if ((OCLVer == kOCLVer::CL20 && OCLCXXVer == kOCLVer::CLCXX10) ||
|
|
(OCLVer == kOCLVer::CL30 && OCLCXXVer == kOCLVer::CLCXX2021))
|
|
return OCLCXXVer;
|
|
report_fatal_error(llvm::Twine(
|
|
"opencl cxx version is not compatible with opencl c version!"));
|
|
}
|
|
return OCLVer;
|
|
}
|
|
|
|
SmallVector<unsigned, 3> decodeMDNode(MDNode *N) {
|
|
if (N == NULL)
|
|
return {};
|
|
size_t NumOperands = N->getNumOperands();
|
|
SmallVector<unsigned, 3> ReadVals;
|
|
ReadVals.reserve(NumOperands);
|
|
for (unsigned I = 0; I < NumOperands; ++I)
|
|
ReadVals.push_back(getMDOperandAsInt(N, I));
|
|
return ReadVals;
|
|
}
|
|
|
|
/// Encode LLVM type by SPIR-V execution mode VecTypeHint
|
|
unsigned encodeVecTypeHint(Type *Ty) {
|
|
if (Ty->isHalfTy())
|
|
return 4;
|
|
if (Ty->isFloatTy())
|
|
return 5;
|
|
if (Ty->isDoubleTy())
|
|
return 6;
|
|
if (IntegerType *IntTy = dyn_cast<IntegerType>(Ty)) {
|
|
switch (IntTy->getIntegerBitWidth()) {
|
|
case 8:
|
|
return 0;
|
|
case 16:
|
|
return 1;
|
|
case 32:
|
|
return 2;
|
|
case 64:
|
|
return 3;
|
|
default:
|
|
llvm_unreachable("invalid integer type");
|
|
}
|
|
}
|
|
if (FixedVectorType *VecTy = dyn_cast<FixedVectorType>(Ty)) {
|
|
Type *EleTy = VecTy->getElementType();
|
|
unsigned Size = VecTy->getNumElements();
|
|
return Size << 16 | encodeVecTypeHint(EleTy);
|
|
}
|
|
llvm_unreachable("invalid type");
|
|
return ~0U;
|
|
}
|
|
|
|
Type *decodeVecTypeHint(LLVMContext &C, unsigned Code) {
|
|
unsigned VecWidth = Code >> 16;
|
|
unsigned Scalar = Code & 0xFFFF;
|
|
Type *ST = nullptr;
|
|
switch (Scalar) {
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
ST = IntegerType::get(C, 1 << (3 + Scalar));
|
|
break;
|
|
case 4:
|
|
ST = Type::getHalfTy(C);
|
|
break;
|
|
case 5:
|
|
ST = Type::getFloatTy(C);
|
|
break;
|
|
case 6:
|
|
ST = Type::getDoubleTy(C);
|
|
break;
|
|
default:
|
|
llvm_unreachable("Invalid vec type hint");
|
|
return nullptr;
|
|
}
|
|
if (VecWidth < 1)
|
|
return ST;
|
|
return FixedVectorType::get(ST, VecWidth);
|
|
}
|
|
|
|
unsigned transVecTypeHint(MDNode *Node) {
|
|
return encodeVecTypeHint(getMDOperandAsType(Node, 0));
|
|
}
|
|
|
|
SPIRAddressSpace getOCLOpaqueTypeAddrSpace(Op OpCode) {
|
|
switch ((unsigned)OpCode) {
|
|
case OpTypeQueue:
|
|
return SPIRV_QUEUE_T_ADDR_SPACE;
|
|
case OpTypeEvent:
|
|
return SPIRV_EVENT_T_ADDR_SPACE;
|
|
case OpTypeDeviceEvent:
|
|
return SPIRV_CLK_EVENT_T_ADDR_SPACE;
|
|
case OpTypeReserveId:
|
|
return SPIRV_RESERVE_ID_T_ADDR_SPACE;
|
|
case OpTypePipe:
|
|
case OpTypePipeStorage:
|
|
return SPIRV_PIPE_ADDR_SPACE;
|
|
case OpTypeImage:
|
|
case OpTypeSampledImage:
|
|
case OpTypeVmeImageINTEL:
|
|
return SPIRV_IMAGE_ADDR_SPACE;
|
|
case OpConstantSampler:
|
|
case OpTypeSampler:
|
|
return SPIRV_SAMPLER_T_ADDR_SPACE;
|
|
case internal::OpTypeJointMatrixINTEL:
|
|
case internal::OpTypeJointMatrixINTELv2:
|
|
case OpTypeCooperativeMatrixKHR:
|
|
case internal::OpTypeTaskSequenceINTEL:
|
|
return SPIRAS_Global;
|
|
default:
|
|
if (isSubgroupAvcINTELTypeOpCode(OpCode))
|
|
return SPIRV_AVC_INTEL_T_ADDR_SPACE;
|
|
assert(false && "No address space is determined for some OCL type");
|
|
return SPIRV_OCL_SPECIAL_TYPES_DEFAULT_ADDR_SPACE;
|
|
}
|
|
}
|
|
|
|
static SPIR::TypeAttributeEnum mapAddrSpaceEnums(SPIRAddressSpace Addrspace) {
|
|
switch (Addrspace) {
|
|
case SPIRAS_Private:
|
|
return SPIR::ATTR_PRIVATE;
|
|
case SPIRAS_Global:
|
|
return SPIR::ATTR_GLOBAL;
|
|
case SPIRAS_Constant:
|
|
return SPIR::ATTR_CONSTANT;
|
|
case SPIRAS_Local:
|
|
return SPIR::ATTR_LOCAL;
|
|
case SPIRAS_Generic:
|
|
return SPIR::ATTR_GENERIC;
|
|
case SPIRAS_GlobalDevice:
|
|
return SPIR::ATTR_GLOBAL_DEVICE;
|
|
case SPIRAS_GlobalHost:
|
|
return SPIR::ATTR_GLOBAL_HOST;
|
|
default:
|
|
llvm_unreachable("Invalid addrspace enum member");
|
|
}
|
|
return SPIR::ATTR_NONE;
|
|
}
|
|
|
|
SPIR::TypeAttributeEnum
|
|
getOCLOpaqueTypeAddrSpace(SPIR::TypePrimitiveEnum Prim) {
|
|
switch (Prim) {
|
|
case SPIR::PRIMITIVE_QUEUE_T:
|
|
return mapAddrSpaceEnums(SPIRV_QUEUE_T_ADDR_SPACE);
|
|
case SPIR::PRIMITIVE_EVENT_T:
|
|
return mapAddrSpaceEnums(SPIRV_EVENT_T_ADDR_SPACE);
|
|
case SPIR::PRIMITIVE_CLK_EVENT_T:
|
|
return mapAddrSpaceEnums(SPIRV_CLK_EVENT_T_ADDR_SPACE);
|
|
case SPIR::PRIMITIVE_RESERVE_ID_T:
|
|
return mapAddrSpaceEnums(SPIRV_RESERVE_ID_T_ADDR_SPACE);
|
|
case SPIR::PRIMITIVE_PIPE_RO_T:
|
|
case SPIR::PRIMITIVE_PIPE_WO_T:
|
|
return mapAddrSpaceEnums(SPIRV_PIPE_ADDR_SPACE);
|
|
case SPIR::PRIMITIVE_IMAGE1D_RO_T:
|
|
case SPIR::PRIMITIVE_IMAGE1D_ARRAY_RO_T:
|
|
case SPIR::PRIMITIVE_IMAGE1D_BUFFER_RO_T:
|
|
case SPIR::PRIMITIVE_IMAGE2D_RO_T:
|
|
case SPIR::PRIMITIVE_IMAGE2D_ARRAY_RO_T:
|
|
case SPIR::PRIMITIVE_IMAGE2D_DEPTH_RO_T:
|
|
case SPIR::PRIMITIVE_IMAGE2D_ARRAY_DEPTH_RO_T:
|
|
case SPIR::PRIMITIVE_IMAGE2D_MSAA_RO_T:
|
|
case SPIR::PRIMITIVE_IMAGE2D_ARRAY_MSAA_RO_T:
|
|
case SPIR::PRIMITIVE_IMAGE2D_MSAA_DEPTH_RO_T:
|
|
case SPIR::PRIMITIVE_IMAGE2D_ARRAY_MSAA_DEPTH_RO_T:
|
|
case SPIR::PRIMITIVE_IMAGE3D_RO_T:
|
|
case SPIR::PRIMITIVE_IMAGE1D_WO_T:
|
|
case SPIR::PRIMITIVE_IMAGE1D_ARRAY_WO_T:
|
|
case SPIR::PRIMITIVE_IMAGE1D_BUFFER_WO_T:
|
|
case SPIR::PRIMITIVE_IMAGE2D_WO_T:
|
|
case SPIR::PRIMITIVE_IMAGE2D_ARRAY_WO_T:
|
|
case SPIR::PRIMITIVE_IMAGE2D_DEPTH_WO_T:
|
|
case SPIR::PRIMITIVE_IMAGE2D_ARRAY_DEPTH_WO_T:
|
|
case SPIR::PRIMITIVE_IMAGE2D_MSAA_WO_T:
|
|
case SPIR::PRIMITIVE_IMAGE2D_ARRAY_MSAA_WO_T:
|
|
case SPIR::PRIMITIVE_IMAGE2D_MSAA_DEPTH_WO_T:
|
|
case SPIR::PRIMITIVE_IMAGE2D_ARRAY_MSAA_DEPTH_WO_T:
|
|
case SPIR::PRIMITIVE_IMAGE3D_WO_T:
|
|
case SPIR::PRIMITIVE_IMAGE1D_RW_T:
|
|
case SPIR::PRIMITIVE_IMAGE1D_ARRAY_RW_T:
|
|
case SPIR::PRIMITIVE_IMAGE1D_BUFFER_RW_T:
|
|
case SPIR::PRIMITIVE_IMAGE2D_RW_T:
|
|
case SPIR::PRIMITIVE_IMAGE2D_ARRAY_RW_T:
|
|
case SPIR::PRIMITIVE_IMAGE2D_DEPTH_RW_T:
|
|
case SPIR::PRIMITIVE_IMAGE2D_ARRAY_DEPTH_RW_T:
|
|
case SPIR::PRIMITIVE_IMAGE2D_MSAA_RW_T:
|
|
case SPIR::PRIMITIVE_IMAGE2D_ARRAY_MSAA_RW_T:
|
|
case SPIR::PRIMITIVE_IMAGE2D_MSAA_DEPTH_RW_T:
|
|
case SPIR::PRIMITIVE_IMAGE2D_ARRAY_MSAA_DEPTH_RW_T:
|
|
case SPIR::PRIMITIVE_IMAGE3D_RW_T:
|
|
return mapAddrSpaceEnums(SPIRV_IMAGE_ADDR_SPACE);
|
|
default:
|
|
llvm_unreachable("No address space is determined for a SPIR primitive");
|
|
}
|
|
return SPIR::ATTR_NONE;
|
|
}
|
|
|
|
// Fetch type of invoke function passed to device execution built-ins
|
|
static FunctionType *getBlockInvokeTy(Function *F, unsigned BlockIdx) {
|
|
auto Params = F->getFunctionType()->params();
|
|
PointerType *FuncPtr = cast<PointerType>(Params[BlockIdx]);
|
|
return FunctionType::get(FuncPtr, Params, false);
|
|
}
|
|
|
|
class OCLBuiltinFuncMangleInfo : public SPIRV::BuiltinFuncMangleInfo {
|
|
public:
|
|
OCLBuiltinFuncMangleInfo(Function *F) : F(F) {}
|
|
OCLBuiltinFuncMangleInfo() = default;
|
|
void init(StringRef UniqName) override {
|
|
// Make a local copy as we will modify the string in init function
|
|
std::string TempStorage = UniqName.str();
|
|
auto NameRef = StringRef(TempStorage);
|
|
|
|
// Helper functions to erase substrings from NameRef (i.e. TempStorage)
|
|
auto EraseSubstring = [&NameRef, &TempStorage](const std::string &ToErase) {
|
|
size_t Pos = TempStorage.find(ToErase);
|
|
if (Pos != std::string::npos) {
|
|
TempStorage.erase(Pos, ToErase.length());
|
|
// re-take StringRef as TempStorage was updated
|
|
NameRef = StringRef(TempStorage);
|
|
}
|
|
};
|
|
auto EraseSymbol = [&NameRef, &TempStorage](size_t Index) {
|
|
TempStorage.erase(Index, 1);
|
|
// re-take StringRef as TempStorage was updated
|
|
NameRef = StringRef(TempStorage);
|
|
};
|
|
|
|
if (NameRef.starts_with("async_work_group")) {
|
|
addUnsignedArg(-1);
|
|
setArgAttr(1, SPIR::ATTR_CONST);
|
|
} else if (NameRef.starts_with("printf"))
|
|
setVarArg(1);
|
|
else if (NameRef.starts_with("write_imageui"))
|
|
addUnsignedArg(2);
|
|
else if (NameRef == "prefetch") {
|
|
addUnsignedArg(1);
|
|
setArgAttr(0, SPIR::ATTR_CONST);
|
|
} else if (NameRef == "get_kernel_work_group_size" ||
|
|
NameRef == "get_kernel_preferred_work_group_size_multiple") {
|
|
assert(F && "lack of necessary information");
|
|
const size_t BlockArgIdx = 0;
|
|
FunctionType *InvokeTy = getBlockInvokeTy(F, BlockArgIdx);
|
|
if (InvokeTy->getNumParams() > 1)
|
|
setLocalArgBlock(BlockArgIdx);
|
|
} else if (NameRef.starts_with("__enqueue_kernel")) {
|
|
// clang doesn't mangle enqueue_kernel builtins
|
|
setAsDontMangle();
|
|
} else if (NameRef.starts_with("get_") || NameRef == "nan" ||
|
|
NameRef == "mem_fence" || NameRef.starts_with("shuffle")) {
|
|
addUnsignedArg(-1);
|
|
if (NameRef.starts_with(kOCLBuiltinName::GetFence)) {
|
|
setArgAttr(0, SPIR::ATTR_CONST);
|
|
addVoidPtrArg(0);
|
|
}
|
|
} else if (NameRef.contains("barrier")) {
|
|
addUnsignedArg(0);
|
|
if (NameRef == "work_group_barrier" || NameRef == "sub_group_barrier" ||
|
|
NameRef == "intel_work_group_barrier_arrive" ||
|
|
NameRef == "intel_work_group_barrier_wait")
|
|
setEnumArg(1, SPIR::PRIMITIVE_MEMORY_SCOPE);
|
|
} else if (NameRef.starts_with("atomic_work_item_fence")) {
|
|
addUnsignedArg(0);
|
|
setEnumArg(1, SPIR::PRIMITIVE_MEMORY_ORDER);
|
|
setEnumArg(2, SPIR::PRIMITIVE_MEMORY_SCOPE);
|
|
} else if (NameRef.starts_with("atom_")) {
|
|
setArgAttr(0, SPIR::ATTR_VOLATILE);
|
|
if (NameRef.ends_with("_umax") || NameRef.ends_with("_umin")) {
|
|
addUnsignedArg(-1);
|
|
// We need to remove u to match OpenCL C built-in function name
|
|
EraseSymbol(5);
|
|
}
|
|
} else if (NameRef.starts_with("atomic")) {
|
|
setArgAttr(0, SPIR::ATTR_VOLATILE);
|
|
if (NameRef.contains("_umax") || NameRef.contains("_umin")) {
|
|
addUnsignedArg(-1);
|
|
// We need to remove u to match OpenCL C built-in function name
|
|
if (NameRef.contains("_fetch"))
|
|
EraseSymbol(13);
|
|
else
|
|
EraseSymbol(7);
|
|
}
|
|
if (NameRef.contains("store_explicit") ||
|
|
NameRef.contains("exchange_explicit") ||
|
|
(NameRef.starts_with("atomic_fetch") &&
|
|
NameRef.contains("explicit"))) {
|
|
setEnumArg(2, SPIR::PRIMITIVE_MEMORY_ORDER);
|
|
setEnumArg(3, SPIR::PRIMITIVE_MEMORY_SCOPE);
|
|
} else if (NameRef.contains("load_explicit") ||
|
|
(NameRef.starts_with("atomic_flag") &&
|
|
NameRef.contains("explicit"))) {
|
|
setEnumArg(1, SPIR::PRIMITIVE_MEMORY_ORDER);
|
|
setEnumArg(2, SPIR::PRIMITIVE_MEMORY_SCOPE);
|
|
} else if (NameRef.ends_with("compare_exchange_strong_explicit") ||
|
|
NameRef.ends_with("compare_exchange_weak_explicit")) {
|
|
setEnumArg(3, SPIR::PRIMITIVE_MEMORY_ORDER);
|
|
setEnumArg(4, SPIR::PRIMITIVE_MEMORY_ORDER);
|
|
setEnumArg(5, SPIR::PRIMITIVE_MEMORY_SCOPE);
|
|
}
|
|
// Don't set atomic property to the first argument of 1.2 atomic
|
|
// built-ins.
|
|
if (!NameRef.ends_with("xchg") && // covers _cmpxchg too
|
|
(NameRef.contains("fetch") ||
|
|
!(NameRef.ends_with("_add") || NameRef.ends_with("_sub") ||
|
|
NameRef.ends_with("_inc") || NameRef.ends_with("_dec") ||
|
|
NameRef.ends_with("_min") || NameRef.ends_with("_max") ||
|
|
NameRef.ends_with("_and") || NameRef.ends_with("_or") ||
|
|
NameRef.ends_with("_xor")))) {
|
|
addAtomicArg(0);
|
|
}
|
|
} else if (NameRef.starts_with("uconvert_")) {
|
|
addUnsignedArg(0);
|
|
NameRef = NameRef.drop_front(1);
|
|
UnmangledName.erase(0, 1);
|
|
} else if (NameRef.starts_with("s_")) {
|
|
if (NameRef == "s_upsample")
|
|
addUnsignedArg(1);
|
|
NameRef = NameRef.drop_front(2);
|
|
} else if (NameRef.starts_with("u_")) {
|
|
addUnsignedArg(-1);
|
|
NameRef = NameRef.drop_front(2);
|
|
} else if (NameRef == "fclamp") {
|
|
NameRef = NameRef.drop_front(1);
|
|
}
|
|
// handle [read|write]pipe builtins (plus two i32 literal args
|
|
// required by SPIR 2.0 provisional specification):
|
|
else if (NameRef == "read_pipe_2" || NameRef == "write_pipe_2") {
|
|
// with 2 arguments (plus two i32 literals):
|
|
// int read_pipe (read_only pipe gentype p, gentype *ptr)
|
|
// int write_pipe (write_only pipe gentype p, const gentype *ptr)
|
|
addVoidPtrArg(1);
|
|
addUnsignedArg(2);
|
|
addUnsignedArg(3);
|
|
// OpenCL-like representation of blocking pipes
|
|
} else if (NameRef == "read_pipe_2_bl" || NameRef == "write_pipe_2_bl") {
|
|
// with 2 arguments (plus two i32 literals):
|
|
// int read_pipe_bl (read_only pipe gentype p, gentype *ptr)
|
|
// int write_pipe_bl (write_only pipe gentype p, const gentype *ptr)
|
|
addVoidPtrArg(1);
|
|
addUnsignedArg(2);
|
|
addUnsignedArg(3);
|
|
} else if (NameRef == "read_pipe_4" || NameRef == "write_pipe_4") {
|
|
// with 4 arguments (plus two i32 literals):
|
|
// int read_pipe (read_only pipe gentype p, reserve_id_t reserve_id, uint
|
|
// index, gentype *ptr) int write_pipe (write_only pipe gentype p,
|
|
// reserve_id_t reserve_id, uint index, const gentype *ptr)
|
|
addUnsignedArg(2);
|
|
addVoidPtrArg(3);
|
|
addUnsignedArg(4);
|
|
addUnsignedArg(5);
|
|
} else if (NameRef.contains("reserve_read_pipe") ||
|
|
NameRef.contains("reserve_write_pipe")) {
|
|
// process [|work_group|sub_group]reserve[read|write]pipe builtins
|
|
addUnsignedArg(1);
|
|
addUnsignedArg(2);
|
|
addUnsignedArg(3);
|
|
} else if (NameRef.contains("commit_read_pipe") ||
|
|
NameRef.contains("commit_write_pipe")) {
|
|
// process [|work_group|sub_group]commit[read|write]pipe builtins
|
|
addUnsignedArg(2);
|
|
addUnsignedArg(3);
|
|
} else if (NameRef == "capture_event_profiling_info") {
|
|
addVoidPtrArg(2);
|
|
setEnumArg(1, SPIR::PRIMITIVE_CLK_PROFILING_INFO);
|
|
} else if (NameRef == "enqueue_marker") {
|
|
setArgAttr(2, SPIR::ATTR_CONST);
|
|
addUnsignedArg(1);
|
|
} else if (NameRef.starts_with("vload")) {
|
|
addUnsignedArg(0);
|
|
setArgAttr(1, SPIR::ATTR_CONST);
|
|
} else if (NameRef.starts_with("vstore")) {
|
|
addUnsignedArg(1);
|
|
} else if (NameRef.starts_with("ndrange_")) {
|
|
addUnsignedArgs(0, 2);
|
|
if (NameRef[8] == '2' || NameRef[8] == '3') {
|
|
setArgAttr(0, SPIR::ATTR_CONST);
|
|
setArgAttr(1, SPIR::ATTR_CONST);
|
|
setArgAttr(2, SPIR::ATTR_CONST);
|
|
}
|
|
} else if (NameRef.contains("umax")) {
|
|
addUnsignedArg(-1);
|
|
EraseSymbol(NameRef.find("umax"));
|
|
} else if (NameRef.contains("umin")) {
|
|
addUnsignedArg(-1);
|
|
EraseSymbol(NameRef.find("umin"));
|
|
} else if (NameRef.contains("broadcast")) {
|
|
addUnsignedArg(-1);
|
|
} else if (NameRef.starts_with(kOCLBuiltinName::SampledReadImage)) {
|
|
if (!NameRef.consume_front(kOCLBuiltinName::Sampled))
|
|
report_fatal_error(llvm::Twine("Builtin name illformed"));
|
|
addSamplerArg(1);
|
|
} else if (NameRef.contains(kOCLSubgroupsAVCIntel::Prefix)) {
|
|
if (NameRef.contains("evaluate_ipe"))
|
|
addSamplerArg(1);
|
|
else if (NameRef.contains("evaluate_with_single_reference"))
|
|
addSamplerArg(2);
|
|
else if (NameRef.contains("evaluate_with_multi_reference")) {
|
|
addUnsignedArg(1);
|
|
std::string PostFix = "_interlaced";
|
|
if (NameRef.contains(PostFix)) {
|
|
addUnsignedArg(2);
|
|
addSamplerArg(3);
|
|
EraseSubstring(PostFix);
|
|
} else
|
|
addSamplerArg(2);
|
|
} else if (NameRef.contains("evaluate_with_dual_reference"))
|
|
addSamplerArg(3);
|
|
else if (NameRef.contains("fme_initialize"))
|
|
addUnsignedArgs(0, 6);
|
|
else if (NameRef.contains("bme_initialize"))
|
|
addUnsignedArgs(0, 7);
|
|
else if (NameRef.contains("set_inter_base_multi_reference_penalty") ||
|
|
NameRef.contains("set_inter_shape_penalty") ||
|
|
NameRef.contains("set_inter_direction_penalty"))
|
|
addUnsignedArg(0);
|
|
else if (NameRef.contains("set_motion_vector_cost_function"))
|
|
addUnsignedArgs(0, 2);
|
|
else if (NameRef.contains("interlaced_field_polarity"))
|
|
addUnsignedArg(0);
|
|
else if (NameRef.contains("interlaced_field_polarities"))
|
|
addUnsignedArgs(0, 1);
|
|
else if (NameRef.contains(kOCLSubgroupsAVCIntel::MCEPrefix)) {
|
|
if (NameRef.contains("get_default"))
|
|
addUnsignedArgs(0, 1);
|
|
} else if (NameRef.contains(kOCLSubgroupsAVCIntel::IMEPrefix)) {
|
|
if (NameRef.contains("initialize"))
|
|
addUnsignedArgs(0, 2);
|
|
else if (NameRef.contains("set_single_reference"))
|
|
addUnsignedArg(1);
|
|
else if (NameRef.contains("set_dual_reference"))
|
|
addUnsignedArg(2);
|
|
else if (NameRef.contains("set_weighted_sad") ||
|
|
NameRef.contains("set_early_search_termination_threshold"))
|
|
addUnsignedArg(0);
|
|
else if (NameRef.contains("adjust_ref_offset"))
|
|
addUnsignedArgs(1, 3);
|
|
else if (NameRef.contains("set_max_motion_vector_count") ||
|
|
NameRef.contains("get_border_reached"))
|
|
addUnsignedArg(0);
|
|
else if (NameRef.contains("shape_distortions") ||
|
|
NameRef.contains("shape_motion_vectors") ||
|
|
NameRef.contains("shape_reference_ids")) {
|
|
if (NameRef.contains("single_reference")) {
|
|
addUnsignedArg(1);
|
|
EraseSubstring("_single_reference");
|
|
} else if (NameRef.contains("dual_reference")) {
|
|
addUnsignedArgs(1, 2);
|
|
EraseSubstring("_dual_reference");
|
|
}
|
|
} else if (NameRef.contains("ref_window_size"))
|
|
addUnsignedArg(0);
|
|
} else if (NameRef.contains(kOCLSubgroupsAVCIntel::SICPrefix)) {
|
|
if (NameRef.contains("initialize") ||
|
|
NameRef.contains("set_intra_luma_shape_penalty"))
|
|
addUnsignedArg(0);
|
|
else if (NameRef.contains("configure_ipe")) {
|
|
if (NameRef.contains("_luma")) {
|
|
addUnsignedArgs(0, 6);
|
|
EraseSubstring("_luma");
|
|
}
|
|
if (NameRef.contains("_chroma")) {
|
|
addUnsignedArgs(7, 9);
|
|
EraseSubstring("_chroma");
|
|
}
|
|
} else if (NameRef.contains("configure_skc"))
|
|
addUnsignedArgs(0, 4);
|
|
else if (NameRef.contains("set_skc")) {
|
|
if (NameRef.contains("forward_transform_enable"))
|
|
addUnsignedArg(0);
|
|
} else if (NameRef.contains("set_block")) {
|
|
if (NameRef.contains("based_raw_skip_sad"))
|
|
addUnsignedArg(0);
|
|
} else if (NameRef.contains("get_motion_vector_mask")) {
|
|
addUnsignedArgs(0, 1);
|
|
} else if (NameRef.contains("luma_mode_cost_function"))
|
|
addUnsignedArgs(0, 2);
|
|
else if (NameRef.contains("chroma_mode_cost_function"))
|
|
addUnsignedArg(0);
|
|
}
|
|
} else if (NameRef.starts_with("intel_sub_group_shuffle")) {
|
|
if (NameRef.ends_with("_down") || NameRef.ends_with("_up"))
|
|
addUnsignedArg(2);
|
|
else
|
|
addUnsignedArg(1);
|
|
} else if (NameRef.starts_with("intel_sub_group_block_write")) {
|
|
// distinguish write to image and other data types based on number of
|
|
// arguments--images have one more argument.
|
|
if (F->getFunctionType()->getNumParams() == 2) {
|
|
addUnsignedArg(0);
|
|
addUnsignedArg(1);
|
|
} else {
|
|
addUnsignedArg(2);
|
|
}
|
|
} else if (NameRef.starts_with("intel_sub_group_block_read")) {
|
|
// distinguish read from image and other data types based on number of
|
|
// arguments--images have one more argument.
|
|
if (F->getFunctionType()->getNumParams() == 1) {
|
|
setArgAttr(0, SPIR::ATTR_CONST);
|
|
addUnsignedArg(0);
|
|
}
|
|
} else if (NameRef.starts_with("intel_sub_group_media_block_write")) {
|
|
addUnsignedArg(3);
|
|
} else if (NameRef.starts_with(kOCLBuiltinName::SubGroupPrefix)) {
|
|
if (NameRef.contains("ballot")) {
|
|
if (NameRef.contains("inverse") || NameRef.contains("bit_count") ||
|
|
NameRef.contains("inclusive_scan") ||
|
|
NameRef.contains("exclusive_scan") ||
|
|
NameRef.contains("find_lsb") || NameRef.contains("find_msb"))
|
|
addUnsignedArg(0);
|
|
else if (NameRef.contains("bit_extract")) {
|
|
addUnsignedArgs(0, 1);
|
|
}
|
|
} else if (NameRef.starts_with("sub_group_clustered_rotate")) {
|
|
addUnsignedArg(2);
|
|
} else if (NameRef.contains("shuffle") || NameRef.contains("clustered"))
|
|
addUnsignedArg(1);
|
|
} else if (NameRef.starts_with("bitfield_insert")) {
|
|
addUnsignedArgs(2, 3);
|
|
} else if (NameRef.starts_with("bitfield_extract_signed") ||
|
|
NameRef.starts_with("bitfield_extract_unsigned")) {
|
|
addUnsignedArgs(1, 2);
|
|
} else if (NameRef.starts_with("dot_")) {
|
|
if (NameRef.contains("4x8packed")) {
|
|
addUnsignedArgs(0, 1);
|
|
if (NameRef == "dot_acc_sat_4x8packed_uu_uint")
|
|
addUnsignedArg(2);
|
|
} else {
|
|
if (NameRef.ends_with("_uu")) {
|
|
addUnsignedArgs(0, 1);
|
|
if (NameRef.starts_with("dot_acc_sat"))
|
|
addUnsignedArg(2);
|
|
} else if (NameRef.ends_with("_su"))
|
|
addUnsignedArg(1);
|
|
NameRef = NameRef.drop_back(std::string("_uu").length());
|
|
}
|
|
}
|
|
|
|
// Store the final version of a function name
|
|
UnmangledName = NameRef.str();
|
|
}
|
|
// Auxiliarry information, it is expected that it is relevant at the moment
|
|
// the init method is called.
|
|
Function *F; // SPIRV decorated function
|
|
};
|
|
|
|
std::unique_ptr<SPIRV::BuiltinFuncMangleInfo> makeMangler(Function &F) {
|
|
return std::make_unique<OCLBuiltinFuncMangleInfo>(&F);
|
|
}
|
|
|
|
bool isSamplerTy(Type *Ty) {
|
|
if (auto *TPT = dyn_cast_or_null<TypedPointerType>(Ty)) {
|
|
auto *STy = dyn_cast_or_null<StructType>(TPT->getElementType());
|
|
return STy && STy->hasName() && STy->getName() == kSPR2TypeName::Sampler;
|
|
}
|
|
if (auto *TET = dyn_cast_or_null<TargetExtType>(Ty)) {
|
|
return TET->getName() == "spirv.Sampler";
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool isPipeOrAddressSpaceCastBI(const StringRef MangledName) {
|
|
return MangledName == "write_pipe_2" || MangledName == "read_pipe_2" ||
|
|
MangledName == "write_pipe_2_bl" || MangledName == "read_pipe_2_bl" ||
|
|
MangledName == "write_pipe_4" || MangledName == "read_pipe_4" ||
|
|
MangledName == "reserve_write_pipe" ||
|
|
MangledName == "reserve_read_pipe" ||
|
|
MangledName == "commit_write_pipe" ||
|
|
MangledName == "commit_read_pipe" ||
|
|
MangledName == "work_group_reserve_write_pipe" ||
|
|
MangledName == "work_group_reserve_read_pipe" ||
|
|
MangledName == "work_group_commit_write_pipe" ||
|
|
MangledName == "work_group_commit_read_pipe" ||
|
|
MangledName == "get_pipe_num_packets_ro" ||
|
|
MangledName == "get_pipe_max_packets_ro" ||
|
|
MangledName == "get_pipe_num_packets_wo" ||
|
|
MangledName == "get_pipe_max_packets_wo" ||
|
|
MangledName == "sub_group_reserve_write_pipe" ||
|
|
MangledName == "sub_group_reserve_read_pipe" ||
|
|
MangledName == "sub_group_commit_write_pipe" ||
|
|
MangledName == "sub_group_commit_read_pipe" ||
|
|
MangledName == "to_global" || MangledName == "to_local" ||
|
|
MangledName == "to_private";
|
|
}
|
|
|
|
bool isEnqueueKernelBI(const StringRef MangledName) {
|
|
return MangledName == "__enqueue_kernel_basic" ||
|
|
MangledName == "__enqueue_kernel_basic_events" ||
|
|
MangledName == "__enqueue_kernel_varargs" ||
|
|
MangledName == "__enqueue_kernel_events_varargs";
|
|
}
|
|
|
|
bool isKernelQueryBI(const StringRef MangledName) {
|
|
return MangledName == "__get_kernel_work_group_size_impl" ||
|
|
MangledName == "__get_kernel_sub_group_count_for_ndrange_impl" ||
|
|
MangledName == "__get_kernel_max_sub_group_size_for_ndrange_impl" ||
|
|
MangledName == "__get_kernel_preferred_work_group_size_multiple_impl";
|
|
}
|
|
|
|
// isUnfusedMulAdd checks if we have the following (most common for fp
|
|
// contranction) pattern in LLVM IR:
|
|
//
|
|
// %mul = fmul float %a, %b
|
|
// %add = fadd float %mul, %c
|
|
//
|
|
// This pattern indicates that fp contraction could have been disabled by
|
|
// #pragma OPENCL FP_CONTRACT OFF. When contraction is enabled (by a pragma or
|
|
// by clang's -ffp-contract=fast), clang would generate:
|
|
//
|
|
// %0 = call float @llvm.fmuladd.f32(float %a, float %b, float %c)
|
|
//
|
|
// or
|
|
//
|
|
// %mul = fmul contract float %a, %b
|
|
// %add = fadd contract float %mul, %c
|
|
//
|
|
// Note that optimizations may form an unfused fmuladd from fadd+load or
|
|
// fadd+call, so this check is quite restrictive (see the comment below).
|
|
//
|
|
bool isUnfusedMulAdd(BinaryOperator *B) {
|
|
if (B->getOpcode() != Instruction::FAdd &&
|
|
B->getOpcode() != Instruction::FSub)
|
|
return false;
|
|
|
|
if (B->hasAllowContract()) {
|
|
// If this fadd or fsub itself has a contract flag, the operation can be
|
|
// contracted regardless of the operands.
|
|
return false;
|
|
}
|
|
|
|
// Otherwise, we cannot easily tell if the operation can be a candidate for
|
|
// contraction or not. Consider the following cases:
|
|
//
|
|
// %mul = alloca float
|
|
// %t1 = fmul float %a, %b
|
|
// store float* %mul, float %t
|
|
// %t2 = load %mul
|
|
// %r = fadd float %t2, %c
|
|
//
|
|
// LLVM IR does not allow %r to be contracted. However, after an optimization
|
|
// it becomes a candidate for contraction if ContractionOFF is not set in
|
|
// SPIR-V:
|
|
//
|
|
// %t1 = fmul float %a, %b
|
|
// %r = fadd float %t1, %c
|
|
//
|
|
// To be on a safe side, we disallow everything that is even remotely similar
|
|
// to fmul + fadd.
|
|
return true;
|
|
}
|
|
|
|
std::string getIntelSubgroupBlockDataPostfix(unsigned ElementBitSize,
|
|
unsigned VectorNumElements) {
|
|
std::ostringstream OSS;
|
|
switch (ElementBitSize) {
|
|
case 8:
|
|
OSS << "_uc";
|
|
break;
|
|
case 16:
|
|
OSS << "_us";
|
|
break;
|
|
case 32:
|
|
// Intentionally does nothing since _ui variant is only an alias.
|
|
break;
|
|
case 64:
|
|
OSS << "_ul";
|
|
break;
|
|
default:
|
|
llvm_unreachable(
|
|
"Incorrect data bitsize for intel_subgroup_block builtins");
|
|
}
|
|
switch (VectorNumElements) {
|
|
case 1:
|
|
break;
|
|
case 2:
|
|
case 4:
|
|
case 8:
|
|
OSS << VectorNumElements;
|
|
break;
|
|
case 16:
|
|
assert((ElementBitSize == 8 || ElementBitSize == 16) &&
|
|
"16 elements vector allowed only for char and short builtins");
|
|
OSS << VectorNumElements;
|
|
break;
|
|
default:
|
|
llvm_unreachable(
|
|
"Incorrect vector length for intel_subgroup_block builtins");
|
|
}
|
|
return OSS.str();
|
|
}
|
|
|
|
void insertImageNameAccessQualifier(SPIRVAccessQualifierKind Acc,
|
|
std::string &Name) {
|
|
std::string QName = rmap<std::string>(Acc);
|
|
// transform: read_only -> ro, write_only -> wo, read_write -> rw
|
|
QName = QName.substr(0, 1) + QName.substr(QName.find("_") + 1, 1) + "_";
|
|
assert(!Name.empty() && "image name should not be empty");
|
|
Name.insert(Name.size() - 1, QName);
|
|
}
|
|
} // namespace OCLUtil
|
|
|
|
Value *SPIRV::transOCLMemScopeIntoSPIRVScope(Value *MemScope,
|
|
std::optional<int> DefaultCase,
|
|
Instruction *InsertBefore) {
|
|
if (auto *C = dyn_cast<ConstantInt>(MemScope)) {
|
|
return ConstantInt::get(
|
|
C->getType(), map<Scope>(static_cast<OCLScopeKind>(C->getZExtValue())));
|
|
}
|
|
|
|
// If memory_scope is not a constant, then we have to insert dynamic mapping:
|
|
return getOrCreateSwitchFunc(kSPIRVName::TranslateOCLMemScope, MemScope,
|
|
OCLMemScopeMap::getMap(), /* IsReverse */ false,
|
|
DefaultCase, InsertBefore);
|
|
}
|
|
|
|
Value *
|
|
SPIRV::transOCLMemOrderIntoSPIRVMemorySemantics(Value *MemOrder,
|
|
std::optional<int> DefaultCase,
|
|
Instruction *InsertBefore) {
|
|
if (auto *C = dyn_cast<ConstantInt>(MemOrder)) {
|
|
return ConstantInt::get(
|
|
C->getType(), mapOCLMemSemanticToSPIRV(
|
|
0, static_cast<OCLMemOrderKind>(C->getZExtValue())));
|
|
}
|
|
|
|
return getOrCreateSwitchFunc(kSPIRVName::TranslateOCLMemOrder, MemOrder,
|
|
OCLMemOrderMap::getMap(), /* IsReverse */ false,
|
|
DefaultCase, InsertBefore);
|
|
}
|
|
|
|
Value *
|
|
SPIRV::transSPIRVMemoryScopeIntoOCLMemoryScope(Value *MemScope,
|
|
Instruction *InsertBefore) {
|
|
if (auto *C = dyn_cast<ConstantInt>(MemScope)) {
|
|
return ConstantInt::get(C->getType(), rmap<OCLScopeKind>(static_cast<Scope>(
|
|
C->getZExtValue())));
|
|
}
|
|
|
|
if (auto *CI = dyn_cast<CallInst>(MemScope)) {
|
|
Function *F = CI->getCalledFunction();
|
|
if (F && F->getName() == kSPIRVName::TranslateOCLMemScope) {
|
|
// In case the SPIR-V module was created from an OpenCL program by
|
|
// *this* SPIR-V generator, we know that the value passed to
|
|
// __translate_ocl_memory_scope is what we should pass to the
|
|
// OpenCL builtin now.
|
|
return CI->getArgOperand(0);
|
|
}
|
|
}
|
|
|
|
return getOrCreateSwitchFunc(
|
|
kSPIRVName::TranslateSPIRVMemScope, MemScope, OCLMemScopeMap::getRMap(),
|
|
/* IsReverse */ true, std::nullopt, InsertBefore);
|
|
}
|
|
|
|
Value *
|
|
SPIRV::transSPIRVMemorySemanticsIntoOCLMemoryOrder(Value *MemorySemantics,
|
|
Instruction *InsertBefore) {
|
|
if (auto *C = dyn_cast<ConstantInt>(MemorySemantics)) {
|
|
return ConstantInt::get(C->getType(),
|
|
mapSPIRVMemSemanticToOCL(C->getZExtValue()).second);
|
|
}
|
|
|
|
if (auto *CI = dyn_cast<CallInst>(MemorySemantics)) {
|
|
Function *F = CI->getCalledFunction();
|
|
if (F && F->getName() == kSPIRVName::TranslateOCLMemOrder) {
|
|
// In case the SPIR-V module was created from an OpenCL program by
|
|
// *this* SPIR-V generator, we know that the value passed to
|
|
// __translate_ocl_memory_order is what we should pass to the
|
|
// OpenCL builtin now.
|
|
return CI->getArgOperand(0);
|
|
}
|
|
}
|
|
|
|
// SPIR-V MemorySemantics contains both OCL mem_fence_flags and mem_order and
|
|
// therefore, we need to apply mask
|
|
int Mask = MemorySemanticsMaskNone | MemorySemanticsAcquireMask |
|
|
MemorySemanticsReleaseMask | MemorySemanticsAcquireReleaseMask |
|
|
MemorySemanticsSequentiallyConsistentMask;
|
|
return getOrCreateSwitchFunc(kSPIRVName::TranslateSPIRVMemOrder,
|
|
MemorySemantics, OCLMemOrderMap::getRMap(),
|
|
/* IsReverse */ true, std::nullopt, InsertBefore,
|
|
Mask);
|
|
}
|
|
|
|
Value *SPIRV::transSPIRVMemorySemanticsIntoOCLMemFenceFlags(
|
|
Value *MemorySemantics, Instruction *InsertBefore) {
|
|
if (auto *C = dyn_cast<ConstantInt>(MemorySemantics)) {
|
|
return ConstantInt::get(C->getType(),
|
|
mapSPIRVMemSemanticToOCL(C->getZExtValue()).first);
|
|
}
|
|
|
|
// TODO: any possible optimizations?
|
|
// SPIR-V MemorySemantics contains both OCL mem_fence_flags and mem_order and
|
|
// therefore, we need to apply mask
|
|
int Mask = MemorySemanticsWorkgroupMemoryMask |
|
|
MemorySemanticsCrossWorkgroupMemoryMask |
|
|
MemorySemanticsImageMemoryMask;
|
|
return getOrCreateSwitchFunc(
|
|
kSPIRVName::TranslateSPIRVMemFence, MemorySemantics,
|
|
OCLMemFenceExtendedMap::getRMap(),
|
|
/* IsReverse */ true, std::nullopt, InsertBefore, Mask);
|
|
}
|
|
|
|
void llvm::mangleOpenClBuiltin(const std::string &UniqName,
|
|
ArrayRef<Type *> ArgTypes,
|
|
std::string &MangledName) {
|
|
OCLUtil::OCLBuiltinFuncMangleInfo BtnInfo;
|
|
MangledName = SPIRV::mangleBuiltin(UniqName, ArgTypes, &BtnInfo);
|
|
}
|