blob: 392adb5f09940792f035bd3cb6fb38f3ed61849b [file] [log] [blame]
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "iree/compiler/Dialect/Vulkan/Utils/TargetEnvUtils.h"
#include "iree/compiler/Dialect/Vulkan/IR/VulkanTypes.h"
#include "mlir/Dialect/SPIRV/SPIRVTypes.h"
#include "mlir/IR/Builders.h"
namespace mlir {
namespace iree_compiler {
namespace IREE {
namespace Vulkan {
namespace {
/// Gets the corresponding SPIR-V version for the ggiven Vulkan target
/// environment.
spirv::Version convertVersion(Vulkan::TargetEnvAttr vkTargetEnv) {
// Vulkan 1.2 supports up to SPIR-V 1.5 by default.
if (vkTargetEnv.getVersion() == Version::V_1_2) return spirv::Version::V_1_5;
// Special extension to enable SPIR-V 1.4.
if (llvm::is_contained(vkTargetEnv.getExtensions(),
Extension::VK_KHR_spirv_1_4))
return spirv::Version::V_1_4;
switch (vkTargetEnv.getVersion()) {
case Version::V_1_0:
// Vulkan 1.0 only supports SPIR-V 1.0 by default.
return spirv::Version::V_1_0;
case Version::V_1_1:
// Vulkan 1.1 supports up to SPIR-V 1.3 by default.
return spirv::Version::V_1_3;
default:
break;
}
llvm_unreachable("unhandled Vulkan version!");
}
/// Gets the corresponding SPIR-V extensions for the given Vulkan target
/// environment.
void convertExtensions(Vulkan::TargetEnvAttr vkTargetEnv,
SmallVectorImpl<spirv::Extension> &extensions) {
extensions.clear();
for (Extension ext : vkTargetEnv.getExtensions()) {
switch (ext) {
case Extension::VK_KHR_16bit_storage:
extensions.push_back(spirv::Extension::SPV_KHR_16bit_storage);
break;
case Extension::VK_KHR_8bit_storage:
extensions.push_back(spirv::Extension::SPV_KHR_8bit_storage);
break;
case Extension::VK_KHR_shader_float16_int8:
// This extension allows using certain SPIR-V capabilities.
break;
case Extension::VK_KHR_spirv_1_4:
// This extension only affects SPIR-V version.
break;
case Extension::VK_KHR_storage_buffer_storage_class:
extensions.push_back(
spirv::Extension::SPV_KHR_storage_buffer_storage_class);
break;
case Extension::VK_KHR_variable_pointers:
extensions.push_back(spirv::Extension::SPV_KHR_variable_pointers);
break;
case Extension::VK_NV_cooperative_matrix:
extensions.push_back(spirv::Extension::SPV_NV_cooperative_matrix);
break;
}
}
}
/// Gets the corresponding SPIR-V capabilities for the given Vulkan target
/// environment.
void convertCapabilities(Vulkan::TargetEnvAttr vkTargetEnv,
SmallVectorImpl<spirv::Capability> &capabilities) {
// Add unconditionally supported capabilities.
// Note that "Table 54. List of SPIR-V Capabilities and enabling features or
// extensions" in the Vulkan spec contains the full list. Right now omit those
// implicitly declared or not useful for us.
capabilities.assign({spirv::Capability::Shader});
auto vkCapabilities = vkTargetEnv.getCapabilitiesAttr();
#define MAP_PRIMITIVE_TYPE(type) \
if (vkCapabilities.shader##type()) \
capabilities.push_back(spirv::Capability::type)
MAP_PRIMITIVE_TYPE(Float64);
MAP_PRIMITIVE_TYPE(Float16);
MAP_PRIMITIVE_TYPE(Int64);
MAP_PRIMITIVE_TYPE(Int16);
MAP_PRIMITIVE_TYPE(Int8);
#undef MAP_PRIMITIVE_TYPE
#define MAP_8_16_BIT_STORAGE(vkFeature, spvCap) \
if (vkCapabilities.vkFeature()) \
capabilities.push_back(spirv::Capability::spvCap)
MAP_8_16_BIT_STORAGE(storageBuffer16BitAccess, StorageBuffer16BitAccess);
MAP_8_16_BIT_STORAGE(uniformAndStorageBuffer16BitAccess, StorageUniform16);
MAP_8_16_BIT_STORAGE(storagePushConstant16, StoragePushConstant16);
MAP_8_16_BIT_STORAGE(storageBuffer8BitAccess, StorageBuffer8BitAccess);
MAP_8_16_BIT_STORAGE(uniformAndStorageBuffer8BitAccess,
UniformAndStorageBuffer8BitAccess);
MAP_8_16_BIT_STORAGE(storagePushConstant8, StoragePushConstant8);
#undef MAP_8_16_BIT_STORAGE
auto subgroupFeatures = *symbolizeSubgroupFeature(
vkCapabilities.subgroupFeatures().getValue().getZExtValue());
#define MAP_SUBGROUP_FEATURE(featureBit) \
if ((subgroupFeatures & SubgroupFeature::featureBit) == \
SubgroupFeature::featureBit) \
capabilities.push_back(spirv::Capability::GroupNonUniform##featureBit)
if ((subgroupFeatures & SubgroupFeature::Basic) == SubgroupFeature::Basic) {
capabilities.push_back(spirv::Capability::GroupNonUniform);
}
MAP_SUBGROUP_FEATURE(Vote);
MAP_SUBGROUP_FEATURE(Arithmetic);
MAP_SUBGROUP_FEATURE(Ballot);
MAP_SUBGROUP_FEATURE(Shuffle);
MAP_SUBGROUP_FEATURE(ShuffleRelative);
MAP_SUBGROUP_FEATURE(Clustered);
MAP_SUBGROUP_FEATURE(Quad);
MAP_SUBGROUP_FEATURE(PartitionedNV);
#undef MAP_SUBGROUP_FEATURE
if (vkCapabilities.variablePointers()) {
capabilities.push_back(spirv::Capability::VariablePointers);
}
if (vkCapabilities.variablePointersStorageBuffer()) {
capabilities.push_back(spirv::Capability::VariablePointersStorageBuffer);
}
if (ArrayAttr attr = vkCapabilities.cooperativeMatrixPropertiesNV()) {
if (!attr.empty()) {
capabilities.push_back(spirv::Capability::CooperativeMatrixNV);
}
}
}
/// Gets the corresponding SPIR-V resource limits for the given Vulkan target
/// environment.
spirv::ResourceLimitsAttr convertResourceLimits(
Vulkan::TargetEnvAttr vkTargetEnv) {
MLIRContext *context = vkTargetEnv.getContext();
auto vkCapabilities = vkTargetEnv.getCapabilitiesAttr();
SmallVector<Attribute, 1> spvAttrs;
if (ArrayAttr attr = vkCapabilities.cooperativeMatrixPropertiesNV()) {
for (auto cooperativeMatrixPropertiesNV :
attr.getAsRange<Vulkan::CooperativeMatrixPropertiesNVAttr>()) {
spvAttrs.push_back(spirv::CooperativeMatrixPropertiesNVAttr::get(
cooperativeMatrixPropertiesNV.mSize(),
cooperativeMatrixPropertiesNV.nSize(),
cooperativeMatrixPropertiesNV.kSize(),
cooperativeMatrixPropertiesNV.aType(),
cooperativeMatrixPropertiesNV.bType(),
cooperativeMatrixPropertiesNV.cType(),
cooperativeMatrixPropertiesNV.resultType(),
cooperativeMatrixPropertiesNV.scope(), context));
}
}
return spirv::ResourceLimitsAttr::get(
vkCapabilities.maxComputeSharedMemorySize(),
vkCapabilities.maxComputeWorkGroupInvocations(),
vkCapabilities.maxComputeWorkGroupSize(), vkCapabilities.subgroupSize(),
ArrayAttr::get(spvAttrs, context), context);
}
} // anonymous namespace
// TODO(antiagainst): The following is good to get us started but it is
// certainly not a scalable way to describe GPU targets. We need more proper
// data structures and such.
const char *getTargetEnvForTriple(llvm::StringRef triple) {
if (triple == "qualcomm-adreno640-unknown-android10") {
// Example profile: https://vulkan.gpuinfo.org/displayreport.php?id=7175
return R"(#vk.target_env<
v1.1, r(87), [
VK_KHR_storage_buffer_storage_class, VK_KHR_variable_pointers
], Qualcomm:IntegratedGPU, {
maxComputeSharedMemorySize = 32768: i32,
maxComputeWorkGroupInvocations = 1024: i32,
maxComputeWorkGroupSize = dense<[1024, 1024, 64]>: vector<3xi32>,
shaderInt16,
subgroupFeatures = 3: i32,
subgroupSize = 64: i32,
variablePointersStorageBuffer, variablePointers
}>)";
}
if (triple == "valhall-g77-unknown-android10") {
// Example profile: https://vulkan.gpuinfo.org/displayreport.php?id=8046
return R"(#vk.target_env<
v1.1, r(108), [
VK_KHR_16bit_storage, VK_KHR_8bit_storage, VK_KHR_shader_float16_int8,
VK_KHR_storage_buffer_storage_class, VK_KHR_variable_pointers
], ARM:IntegratedGPU, {
maxComputeSharedMemorySize = 32768: i32,
maxComputeWorkGroupInvocations = 512: i32,
maxComputeWorkGroupSize = dense<[512, 512, 512]>: vector<3xi32>,
shaderInt16,
subgroupFeatures = 1: i32,
subgroupSize = 16: i32,
storageBuffer16BitAccess, storagePushConstant16,
uniformAndStorageBuffer16BitAccess,
storageBuffer8BitAccess, uniformAndStorageBuffer8BitAccess,
storagePushConstant8,
shaderFloat16, shaderInt8,
variablePointersStorageBuffer, variablePointers
}>)";
}
if (triple == "swiftshader-unknown-unknown") {
// Example profile: https://vulkan.gpuinfo.org/displayreport.php?id=9095
return R"(#vk.target_env<
v1.1, r(0), [VK_KHR_storage_buffer_storage_class], SwiftShader:CPU, {
maxComputeSharedMemorySize = 16384: i32,
maxComputeWorkGroupInvocations = 128: i32,
maxComputeWorkGroupSize = dense<[128, 128, 64]>: vector<3xi32>,
subgroupFeatures = 63: i32,
subgroupSize = 4: i32
}>)";
}
if (triple == "turing-t4-unknown-linux") {
return R"(#vk.target_env<
v1.2, r(133), [
VK_KHR_16bit_storage, VK_KHR_8bit_storage, VK_KHR_shader_float16_int8,
VK_KHR_spirv_1_4, VK_KHR_storage_buffer_storage_class,
VK_KHR_variable_pointers, VK_NV_cooperative_matrix], NVIDIA:DiscreteGPU, {
maxComputeSharedMemorySize = 49152: i32,
maxComputeWorkGroupInvocations = 1024: i32,
maxComputeWorkGroupSize = dense<[2147483647, 65535, 65535]> : vector<3xi32>,
shaderFloat64, shaderInt16, shaderInt64,
subgroupFeatures = 63: i32, subgroupSize = 32: i32,
storageBuffer16BitAccess, storagePushConstant16,
uniformAndStorageBuffer16BitAccess,
storageBuffer8BitAccess, storagePushConstant8,
uniformAndStorageBuffer8BitAccess,
shaderFloat16, shaderInt8,
variablePointersStorageBuffer, variablePointers,
cooperativeMatrixPropertiesNV = [{
mSize = 8: i32, nSize = 8: i32, kSize = 32: i32, aType = i8,
bType = i8, cType = i32, resultType = i32, scope = 3: i32
}, {
mSize = 8: i32, nSize = 8: i32, kSize = 16: i32, aType = f16,
bType = f16, cType = f16, resultType = f16, scope = 3: i32
}, {
mSize = 8: i32, nSize = 8: i32, kSize = 16: i32, aType = f16,
bType = f16, cType = f32, resultType = f32, scope = 3: i32
}]
}>)";
}
return nullptr;
}
spirv::TargetEnvAttr convertTargetEnv(Vulkan::TargetEnvAttr vkTargetEnv) {
auto spvVersion = convertVersion(vkTargetEnv);
SmallVector<spirv::Extension, 4> spvExtensions;
convertExtensions(vkTargetEnv, spvExtensions);
SmallVector<spirv::Capability, 8> spvCapabilities;
convertCapabilities(vkTargetEnv, spvCapabilities);
auto spvLimits = convertResourceLimits(vkTargetEnv);
auto triple = spirv::VerCapExtAttr::get(
spvVersion, spvCapabilities, spvExtensions, vkTargetEnv.getContext());
return spirv::TargetEnvAttr::get(triple, vkTargetEnv.getVendorID(),
vkTargetEnv.getDeviceType(),
vkTargetEnv.getDeviceID(), spvLimits);
}
} // namespace Vulkan
} // namespace IREE
} // namespace iree_compiler
} // namespace mlir