|  | //===- AMDGPUMetadataVerifier.cpp - MsgPack Types ---------------*- C++ -*-===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | /// \file | 
|  | /// Implements a verifier for AMDGPU HSA metadata. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "llvm/BinaryFormat/AMDGPUMetadataVerifier.h" | 
|  | #include "llvm/Support/AMDGPUMetadata.h" | 
|  |  | 
|  | namespace llvm { | 
|  | namespace AMDGPU { | 
|  | namespace HSAMD { | 
|  | namespace V3 { | 
|  |  | 
|  | bool MetadataVerifier::verifyScalar( | 
|  | msgpack::DocNode &Node, msgpack::Type SKind, | 
|  | function_ref<bool(msgpack::DocNode &)> verifyValue) { | 
|  | if (!Node.isScalar()) | 
|  | return false; | 
|  | if (Node.getKind() != SKind) { | 
|  | if (Strict) | 
|  | return false; | 
|  | // If we are not strict, we interpret string values as "implicitly typed" | 
|  | // and attempt to coerce them to the expected type here. | 
|  | if (Node.getKind() != msgpack::Type::String) | 
|  | return false; | 
|  | StringRef StringValue = Node.getString(); | 
|  | Node.fromString(StringValue); | 
|  | if (Node.getKind() != SKind) | 
|  | return false; | 
|  | } | 
|  | if (verifyValue) | 
|  | return verifyValue(Node); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool MetadataVerifier::verifyInteger(msgpack::DocNode &Node) { | 
|  | if (!verifyScalar(Node, msgpack::Type::UInt)) | 
|  | if (!verifyScalar(Node, msgpack::Type::Int)) | 
|  | return false; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool MetadataVerifier::verifyArray( | 
|  | msgpack::DocNode &Node, function_ref<bool(msgpack::DocNode &)> verifyNode, | 
|  | Optional<size_t> Size) { | 
|  | if (!Node.isArray()) | 
|  | return false; | 
|  | auto &Array = Node.getArray(); | 
|  | if (Size && Array.size() != *Size) | 
|  | return false; | 
|  | for (auto &Item : Array) | 
|  | if (!verifyNode(Item)) | 
|  | return false; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool MetadataVerifier::verifyEntry( | 
|  | msgpack::MapDocNode &MapNode, StringRef Key, bool Required, | 
|  | function_ref<bool(msgpack::DocNode &)> verifyNode) { | 
|  | auto Entry = MapNode.find(Key); | 
|  | if (Entry == MapNode.end()) | 
|  | return !Required; | 
|  | return verifyNode(Entry->second); | 
|  | } | 
|  |  | 
|  | bool MetadataVerifier::verifyScalarEntry( | 
|  | msgpack::MapDocNode &MapNode, StringRef Key, bool Required, | 
|  | msgpack::Type SKind, | 
|  | function_ref<bool(msgpack::DocNode &)> verifyValue) { | 
|  | return verifyEntry(MapNode, Key, Required, [=](msgpack::DocNode &Node) { | 
|  | return verifyScalar(Node, SKind, verifyValue); | 
|  | }); | 
|  | } | 
|  |  | 
|  | bool MetadataVerifier::verifyIntegerEntry(msgpack::MapDocNode &MapNode, | 
|  | StringRef Key, bool Required) { | 
|  | return verifyEntry(MapNode, Key, Required, [this](msgpack::DocNode &Node) { | 
|  | return verifyInteger(Node); | 
|  | }); | 
|  | } | 
|  |  | 
|  | bool MetadataVerifier::verifyKernelArgs(msgpack::DocNode &Node) { | 
|  | if (!Node.isMap()) | 
|  | return false; | 
|  | auto &ArgsMap = Node.getMap(); | 
|  |  | 
|  | if (!verifyScalarEntry(ArgsMap, ".name", false, | 
|  | msgpack::Type::String)) | 
|  | return false; | 
|  | if (!verifyScalarEntry(ArgsMap, ".type_name", false, | 
|  | msgpack::Type::String)) | 
|  | return false; | 
|  | if (!verifyIntegerEntry(ArgsMap, ".size", true)) | 
|  | return false; | 
|  | if (!verifyIntegerEntry(ArgsMap, ".offset", true)) | 
|  | return false; | 
|  | if (!verifyScalarEntry(ArgsMap, ".value_kind", true, | 
|  | msgpack::Type::String, | 
|  | [](msgpack::DocNode &SNode) { | 
|  | return StringSwitch<bool>(SNode.getString()) | 
|  | .Case("by_value", true) | 
|  | .Case("global_buffer", true) | 
|  | .Case("dynamic_shared_pointer", true) | 
|  | .Case("sampler", true) | 
|  | .Case("image", true) | 
|  | .Case("pipe", true) | 
|  | .Case("queue", true) | 
|  | .Case("hidden_global_offset_x", true) | 
|  | .Case("hidden_global_offset_y", true) | 
|  | .Case("hidden_global_offset_z", true) | 
|  | .Case("hidden_none", true) | 
|  | .Case("hidden_printf_buffer", true) | 
|  | .Case("hidden_default_queue", true) | 
|  | .Case("hidden_completion_action", true) | 
|  | .Case("hidden_multigrid_sync_arg", true) | 
|  | .Default(false); | 
|  | })) | 
|  | return false; | 
|  | if (!verifyScalarEntry(ArgsMap, ".value_type", true, | 
|  | msgpack::Type::String, | 
|  | [](msgpack::DocNode &SNode) { | 
|  | return StringSwitch<bool>(SNode.getString()) | 
|  | .Case("struct", true) | 
|  | .Case("i8", true) | 
|  | .Case("u8", true) | 
|  | .Case("i16", true) | 
|  | .Case("u16", true) | 
|  | .Case("f16", true) | 
|  | .Case("i32", true) | 
|  | .Case("u32", true) | 
|  | .Case("f32", true) | 
|  | .Case("i64", true) | 
|  | .Case("u64", true) | 
|  | .Case("f64", true) | 
|  | .Default(false); | 
|  | })) | 
|  | return false; | 
|  | if (!verifyIntegerEntry(ArgsMap, ".pointee_align", false)) | 
|  | return false; | 
|  | if (!verifyScalarEntry(ArgsMap, ".address_space", false, | 
|  | msgpack::Type::String, | 
|  | [](msgpack::DocNode &SNode) { | 
|  | return StringSwitch<bool>(SNode.getString()) | 
|  | .Case("private", true) | 
|  | .Case("global", true) | 
|  | .Case("constant", true) | 
|  | .Case("local", true) | 
|  | .Case("generic", true) | 
|  | .Case("region", true) | 
|  | .Default(false); | 
|  | })) | 
|  | return false; | 
|  | if (!verifyScalarEntry(ArgsMap, ".access", false, | 
|  | msgpack::Type::String, | 
|  | [](msgpack::DocNode &SNode) { | 
|  | return StringSwitch<bool>(SNode.getString()) | 
|  | .Case("read_only", true) | 
|  | .Case("write_only", true) | 
|  | .Case("read_write", true) | 
|  | .Default(false); | 
|  | })) | 
|  | return false; | 
|  | if (!verifyScalarEntry(ArgsMap, ".actual_access", false, | 
|  | msgpack::Type::String, | 
|  | [](msgpack::DocNode &SNode) { | 
|  | return StringSwitch<bool>(SNode.getString()) | 
|  | .Case("read_only", true) | 
|  | .Case("write_only", true) | 
|  | .Case("read_write", true) | 
|  | .Default(false); | 
|  | })) | 
|  | return false; | 
|  | if (!verifyScalarEntry(ArgsMap, ".is_const", false, | 
|  | msgpack::Type::Boolean)) | 
|  | return false; | 
|  | if (!verifyScalarEntry(ArgsMap, ".is_restrict", false, | 
|  | msgpack::Type::Boolean)) | 
|  | return false; | 
|  | if (!verifyScalarEntry(ArgsMap, ".is_volatile", false, | 
|  | msgpack::Type::Boolean)) | 
|  | return false; | 
|  | if (!verifyScalarEntry(ArgsMap, ".is_pipe", false, | 
|  | msgpack::Type::Boolean)) | 
|  | return false; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool MetadataVerifier::verifyKernel(msgpack::DocNode &Node) { | 
|  | if (!Node.isMap()) | 
|  | return false; | 
|  | auto &KernelMap = Node.getMap(); | 
|  |  | 
|  | if (!verifyScalarEntry(KernelMap, ".name", true, | 
|  | msgpack::Type::String)) | 
|  | return false; | 
|  | if (!verifyScalarEntry(KernelMap, ".symbol", true, | 
|  | msgpack::Type::String)) | 
|  | return false; | 
|  | if (!verifyScalarEntry(KernelMap, ".language", false, | 
|  | msgpack::Type::String, | 
|  | [](msgpack::DocNode &SNode) { | 
|  | return StringSwitch<bool>(SNode.getString()) | 
|  | .Case("OpenCL C", true) | 
|  | .Case("OpenCL C++", true) | 
|  | .Case("HCC", true) | 
|  | .Case("HIP", true) | 
|  | .Case("OpenMP", true) | 
|  | .Case("Assembler", true) | 
|  | .Default(false); | 
|  | })) | 
|  | return false; | 
|  | if (!verifyEntry( | 
|  | KernelMap, ".language_version", false, [this](msgpack::DocNode &Node) { | 
|  | return verifyArray( | 
|  | Node, | 
|  | [this](msgpack::DocNode &Node) { return verifyInteger(Node); }, 2); | 
|  | })) | 
|  | return false; | 
|  | if (!verifyEntry(KernelMap, ".args", false, [this](msgpack::DocNode &Node) { | 
|  | return verifyArray(Node, [this](msgpack::DocNode &Node) { | 
|  | return verifyKernelArgs(Node); | 
|  | }); | 
|  | })) | 
|  | return false; | 
|  | if (!verifyEntry(KernelMap, ".reqd_workgroup_size", false, | 
|  | [this](msgpack::DocNode &Node) { | 
|  | return verifyArray(Node, | 
|  | [this](msgpack::DocNode &Node) { | 
|  | return verifyInteger(Node); | 
|  | }, | 
|  | 3); | 
|  | })) | 
|  | return false; | 
|  | if (!verifyEntry(KernelMap, ".workgroup_size_hint", false, | 
|  | [this](msgpack::DocNode &Node) { | 
|  | return verifyArray(Node, | 
|  | [this](msgpack::DocNode &Node) { | 
|  | return verifyInteger(Node); | 
|  | }, | 
|  | 3); | 
|  | })) | 
|  | return false; | 
|  | if (!verifyScalarEntry(KernelMap, ".vec_type_hint", false, | 
|  | msgpack::Type::String)) | 
|  | return false; | 
|  | if (!verifyScalarEntry(KernelMap, ".device_enqueue_symbol", false, | 
|  | msgpack::Type::String)) | 
|  | return false; | 
|  | if (!verifyIntegerEntry(KernelMap, ".kernarg_segment_size", true)) | 
|  | return false; | 
|  | if (!verifyIntegerEntry(KernelMap, ".group_segment_fixed_size", true)) | 
|  | return false; | 
|  | if (!verifyIntegerEntry(KernelMap, ".private_segment_fixed_size", true)) | 
|  | return false; | 
|  | if (!verifyIntegerEntry(KernelMap, ".kernarg_segment_align", true)) | 
|  | return false; | 
|  | if (!verifyIntegerEntry(KernelMap, ".wavefront_size", true)) | 
|  | return false; | 
|  | if (!verifyIntegerEntry(KernelMap, ".sgpr_count", true)) | 
|  | return false; | 
|  | if (!verifyIntegerEntry(KernelMap, ".vgpr_count", true)) | 
|  | return false; | 
|  | if (!verifyIntegerEntry(KernelMap, ".max_flat_workgroup_size", true)) | 
|  | return false; | 
|  | if (!verifyIntegerEntry(KernelMap, ".sgpr_spill_count", false)) | 
|  | return false; | 
|  | if (!verifyIntegerEntry(KernelMap, ".vgpr_spill_count", false)) | 
|  | return false; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool MetadataVerifier::verify(msgpack::DocNode &HSAMetadataRoot) { | 
|  | if (!HSAMetadataRoot.isMap()) | 
|  | return false; | 
|  | auto &RootMap = HSAMetadataRoot.getMap(); | 
|  |  | 
|  | if (!verifyEntry( | 
|  | RootMap, "amdhsa.version", true, [this](msgpack::DocNode &Node) { | 
|  | return verifyArray( | 
|  | Node, | 
|  | [this](msgpack::DocNode &Node) { return verifyInteger(Node); }, 2); | 
|  | })) | 
|  | return false; | 
|  | if (!verifyEntry( | 
|  | RootMap, "amdhsa.printf", false, [this](msgpack::DocNode &Node) { | 
|  | return verifyArray(Node, [this](msgpack::DocNode &Node) { | 
|  | return verifyScalar(Node, msgpack::Type::String); | 
|  | }); | 
|  | })) | 
|  | return false; | 
|  | if (!verifyEntry(RootMap, "amdhsa.kernels", true, | 
|  | [this](msgpack::DocNode &Node) { | 
|  | return verifyArray(Node, [this](msgpack::DocNode &Node) { | 
|  | return verifyKernel(Node); | 
|  | }); | 
|  | })) | 
|  | return false; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | } // end namespace V3 | 
|  | } // end namespace HSAMD | 
|  | } // end namespace AMDGPU | 
|  | } // end namespace llvm |