106f32e7eSjoerg //===- AMDGPUMetadataVerifier.cpp - MsgPack Types ---------------*- C++ -*-===//
206f32e7eSjoerg //
306f32e7eSjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
406f32e7eSjoerg // See https://llvm.org/LICENSE.txt for license information.
506f32e7eSjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
606f32e7eSjoerg //
706f32e7eSjoerg //===----------------------------------------------------------------------===//
806f32e7eSjoerg //
906f32e7eSjoerg /// \file
1006f32e7eSjoerg /// Implements a verifier for AMDGPU HSA metadata.
1106f32e7eSjoerg //
1206f32e7eSjoerg //===----------------------------------------------------------------------===//
1306f32e7eSjoerg 
1406f32e7eSjoerg #include "llvm/BinaryFormat/AMDGPUMetadataVerifier.h"
15*da58b97aSjoerg #include "llvm/ADT/StringSwitch.h"
1606f32e7eSjoerg #include "llvm/Support/AMDGPUMetadata.h"
1706f32e7eSjoerg 
1806f32e7eSjoerg namespace llvm {
1906f32e7eSjoerg namespace AMDGPU {
2006f32e7eSjoerg namespace HSAMD {
2106f32e7eSjoerg namespace V3 {
2206f32e7eSjoerg 
verifyScalar(msgpack::DocNode & Node,msgpack::Type SKind,function_ref<bool (msgpack::DocNode &)> verifyValue)2306f32e7eSjoerg bool MetadataVerifier::verifyScalar(
2406f32e7eSjoerg     msgpack::DocNode &Node, msgpack::Type SKind,
2506f32e7eSjoerg     function_ref<bool(msgpack::DocNode &)> verifyValue) {
2606f32e7eSjoerg   if (!Node.isScalar())
2706f32e7eSjoerg     return false;
2806f32e7eSjoerg   if (Node.getKind() != SKind) {
2906f32e7eSjoerg     if (Strict)
3006f32e7eSjoerg       return false;
3106f32e7eSjoerg     // If we are not strict, we interpret string values as "implicitly typed"
3206f32e7eSjoerg     // and attempt to coerce them to the expected type here.
3306f32e7eSjoerg     if (Node.getKind() != msgpack::Type::String)
3406f32e7eSjoerg       return false;
3506f32e7eSjoerg     StringRef StringValue = Node.getString();
3606f32e7eSjoerg     Node.fromString(StringValue);
3706f32e7eSjoerg     if (Node.getKind() != SKind)
3806f32e7eSjoerg       return false;
3906f32e7eSjoerg   }
4006f32e7eSjoerg   if (verifyValue)
4106f32e7eSjoerg     return verifyValue(Node);
4206f32e7eSjoerg   return true;
4306f32e7eSjoerg }
4406f32e7eSjoerg 
verifyInteger(msgpack::DocNode & Node)4506f32e7eSjoerg bool MetadataVerifier::verifyInteger(msgpack::DocNode &Node) {
4606f32e7eSjoerg   if (!verifyScalar(Node, msgpack::Type::UInt))
4706f32e7eSjoerg     if (!verifyScalar(Node, msgpack::Type::Int))
4806f32e7eSjoerg       return false;
4906f32e7eSjoerg   return true;
5006f32e7eSjoerg }
5106f32e7eSjoerg 
verifyArray(msgpack::DocNode & Node,function_ref<bool (msgpack::DocNode &)> verifyNode,Optional<size_t> Size)5206f32e7eSjoerg bool MetadataVerifier::verifyArray(
5306f32e7eSjoerg     msgpack::DocNode &Node, function_ref<bool(msgpack::DocNode &)> verifyNode,
5406f32e7eSjoerg     Optional<size_t> Size) {
5506f32e7eSjoerg   if (!Node.isArray())
5606f32e7eSjoerg     return false;
5706f32e7eSjoerg   auto &Array = Node.getArray();
5806f32e7eSjoerg   if (Size && Array.size() != *Size)
5906f32e7eSjoerg     return false;
6006f32e7eSjoerg   for (auto &Item : Array)
6106f32e7eSjoerg     if (!verifyNode(Item))
6206f32e7eSjoerg       return false;
6306f32e7eSjoerg 
6406f32e7eSjoerg   return true;
6506f32e7eSjoerg }
6606f32e7eSjoerg 
verifyEntry(msgpack::MapDocNode & MapNode,StringRef Key,bool Required,function_ref<bool (msgpack::DocNode &)> verifyNode)6706f32e7eSjoerg bool MetadataVerifier::verifyEntry(
6806f32e7eSjoerg     msgpack::MapDocNode &MapNode, StringRef Key, bool Required,
6906f32e7eSjoerg     function_ref<bool(msgpack::DocNode &)> verifyNode) {
7006f32e7eSjoerg   auto Entry = MapNode.find(Key);
7106f32e7eSjoerg   if (Entry == MapNode.end())
7206f32e7eSjoerg     return !Required;
7306f32e7eSjoerg   return verifyNode(Entry->second);
7406f32e7eSjoerg }
7506f32e7eSjoerg 
verifyScalarEntry(msgpack::MapDocNode & MapNode,StringRef Key,bool Required,msgpack::Type SKind,function_ref<bool (msgpack::DocNode &)> verifyValue)7606f32e7eSjoerg bool MetadataVerifier::verifyScalarEntry(
7706f32e7eSjoerg     msgpack::MapDocNode &MapNode, StringRef Key, bool Required,
7806f32e7eSjoerg     msgpack::Type SKind,
7906f32e7eSjoerg     function_ref<bool(msgpack::DocNode &)> verifyValue) {
8006f32e7eSjoerg   return verifyEntry(MapNode, Key, Required, [=](msgpack::DocNode &Node) {
8106f32e7eSjoerg     return verifyScalar(Node, SKind, verifyValue);
8206f32e7eSjoerg   });
8306f32e7eSjoerg }
8406f32e7eSjoerg 
verifyIntegerEntry(msgpack::MapDocNode & MapNode,StringRef Key,bool Required)8506f32e7eSjoerg bool MetadataVerifier::verifyIntegerEntry(msgpack::MapDocNode &MapNode,
8606f32e7eSjoerg                                           StringRef Key, bool Required) {
8706f32e7eSjoerg   return verifyEntry(MapNode, Key, Required, [this](msgpack::DocNode &Node) {
8806f32e7eSjoerg     return verifyInteger(Node);
8906f32e7eSjoerg   });
9006f32e7eSjoerg }
9106f32e7eSjoerg 
verifyKernelArgs(msgpack::DocNode & Node)9206f32e7eSjoerg bool MetadataVerifier::verifyKernelArgs(msgpack::DocNode &Node) {
9306f32e7eSjoerg   if (!Node.isMap())
9406f32e7eSjoerg     return false;
9506f32e7eSjoerg   auto &ArgsMap = Node.getMap();
9606f32e7eSjoerg 
9706f32e7eSjoerg   if (!verifyScalarEntry(ArgsMap, ".name", false,
9806f32e7eSjoerg                          msgpack::Type::String))
9906f32e7eSjoerg     return false;
10006f32e7eSjoerg   if (!verifyScalarEntry(ArgsMap, ".type_name", false,
10106f32e7eSjoerg                          msgpack::Type::String))
10206f32e7eSjoerg     return false;
10306f32e7eSjoerg   if (!verifyIntegerEntry(ArgsMap, ".size", true))
10406f32e7eSjoerg     return false;
10506f32e7eSjoerg   if (!verifyIntegerEntry(ArgsMap, ".offset", true))
10606f32e7eSjoerg     return false;
10706f32e7eSjoerg   if (!verifyScalarEntry(ArgsMap, ".value_kind", true,
10806f32e7eSjoerg                          msgpack::Type::String,
10906f32e7eSjoerg                          [](msgpack::DocNode &SNode) {
11006f32e7eSjoerg                            return StringSwitch<bool>(SNode.getString())
11106f32e7eSjoerg                                .Case("by_value", true)
11206f32e7eSjoerg                                .Case("global_buffer", true)
11306f32e7eSjoerg                                .Case("dynamic_shared_pointer", true)
11406f32e7eSjoerg                                .Case("sampler", true)
11506f32e7eSjoerg                                .Case("image", true)
11606f32e7eSjoerg                                .Case("pipe", true)
11706f32e7eSjoerg                                .Case("queue", true)
11806f32e7eSjoerg                                .Case("hidden_global_offset_x", true)
11906f32e7eSjoerg                                .Case("hidden_global_offset_y", true)
12006f32e7eSjoerg                                .Case("hidden_global_offset_z", true)
12106f32e7eSjoerg                                .Case("hidden_none", true)
12206f32e7eSjoerg                                .Case("hidden_printf_buffer", true)
123*da58b97aSjoerg                                .Case("hidden_hostcall_buffer", true)
12406f32e7eSjoerg                                .Case("hidden_default_queue", true)
12506f32e7eSjoerg                                .Case("hidden_completion_action", true)
12606f32e7eSjoerg                                .Case("hidden_multigrid_sync_arg", true)
12706f32e7eSjoerg                                .Default(false);
12806f32e7eSjoerg                          }))
12906f32e7eSjoerg     return false;
13006f32e7eSjoerg   if (!verifyIntegerEntry(ArgsMap, ".pointee_align", false))
13106f32e7eSjoerg     return false;
13206f32e7eSjoerg   if (!verifyScalarEntry(ArgsMap, ".address_space", false,
13306f32e7eSjoerg                          msgpack::Type::String,
13406f32e7eSjoerg                          [](msgpack::DocNode &SNode) {
13506f32e7eSjoerg                            return StringSwitch<bool>(SNode.getString())
13606f32e7eSjoerg                                .Case("private", true)
13706f32e7eSjoerg                                .Case("global", true)
13806f32e7eSjoerg                                .Case("constant", true)
13906f32e7eSjoerg                                .Case("local", true)
14006f32e7eSjoerg                                .Case("generic", true)
14106f32e7eSjoerg                                .Case("region", true)
14206f32e7eSjoerg                                .Default(false);
14306f32e7eSjoerg                          }))
14406f32e7eSjoerg     return false;
14506f32e7eSjoerg   if (!verifyScalarEntry(ArgsMap, ".access", false,
14606f32e7eSjoerg                          msgpack::Type::String,
14706f32e7eSjoerg                          [](msgpack::DocNode &SNode) {
14806f32e7eSjoerg                            return StringSwitch<bool>(SNode.getString())
14906f32e7eSjoerg                                .Case("read_only", true)
15006f32e7eSjoerg                                .Case("write_only", true)
15106f32e7eSjoerg                                .Case("read_write", true)
15206f32e7eSjoerg                                .Default(false);
15306f32e7eSjoerg                          }))
15406f32e7eSjoerg     return false;
15506f32e7eSjoerg   if (!verifyScalarEntry(ArgsMap, ".actual_access", false,
15606f32e7eSjoerg                          msgpack::Type::String,
15706f32e7eSjoerg                          [](msgpack::DocNode &SNode) {
15806f32e7eSjoerg                            return StringSwitch<bool>(SNode.getString())
15906f32e7eSjoerg                                .Case("read_only", true)
16006f32e7eSjoerg                                .Case("write_only", true)
16106f32e7eSjoerg                                .Case("read_write", true)
16206f32e7eSjoerg                                .Default(false);
16306f32e7eSjoerg                          }))
16406f32e7eSjoerg     return false;
16506f32e7eSjoerg   if (!verifyScalarEntry(ArgsMap, ".is_const", false,
16606f32e7eSjoerg                          msgpack::Type::Boolean))
16706f32e7eSjoerg     return false;
16806f32e7eSjoerg   if (!verifyScalarEntry(ArgsMap, ".is_restrict", false,
16906f32e7eSjoerg                          msgpack::Type::Boolean))
17006f32e7eSjoerg     return false;
17106f32e7eSjoerg   if (!verifyScalarEntry(ArgsMap, ".is_volatile", false,
17206f32e7eSjoerg                          msgpack::Type::Boolean))
17306f32e7eSjoerg     return false;
17406f32e7eSjoerg   if (!verifyScalarEntry(ArgsMap, ".is_pipe", false,
17506f32e7eSjoerg                          msgpack::Type::Boolean))
17606f32e7eSjoerg     return false;
17706f32e7eSjoerg 
17806f32e7eSjoerg   return true;
17906f32e7eSjoerg }
18006f32e7eSjoerg 
verifyKernel(msgpack::DocNode & Node)18106f32e7eSjoerg bool MetadataVerifier::verifyKernel(msgpack::DocNode &Node) {
18206f32e7eSjoerg   if (!Node.isMap())
18306f32e7eSjoerg     return false;
18406f32e7eSjoerg   auto &KernelMap = Node.getMap();
18506f32e7eSjoerg 
18606f32e7eSjoerg   if (!verifyScalarEntry(KernelMap, ".name", true,
18706f32e7eSjoerg                          msgpack::Type::String))
18806f32e7eSjoerg     return false;
18906f32e7eSjoerg   if (!verifyScalarEntry(KernelMap, ".symbol", true,
19006f32e7eSjoerg                          msgpack::Type::String))
19106f32e7eSjoerg     return false;
19206f32e7eSjoerg   if (!verifyScalarEntry(KernelMap, ".language", false,
19306f32e7eSjoerg                          msgpack::Type::String,
19406f32e7eSjoerg                          [](msgpack::DocNode &SNode) {
19506f32e7eSjoerg                            return StringSwitch<bool>(SNode.getString())
19606f32e7eSjoerg                                .Case("OpenCL C", true)
19706f32e7eSjoerg                                .Case("OpenCL C++", true)
19806f32e7eSjoerg                                .Case("HCC", true)
19906f32e7eSjoerg                                .Case("HIP", true)
20006f32e7eSjoerg                                .Case("OpenMP", true)
20106f32e7eSjoerg                                .Case("Assembler", true)
20206f32e7eSjoerg                                .Default(false);
20306f32e7eSjoerg                          }))
20406f32e7eSjoerg     return false;
20506f32e7eSjoerg   if (!verifyEntry(
20606f32e7eSjoerg           KernelMap, ".language_version", false, [this](msgpack::DocNode &Node) {
20706f32e7eSjoerg             return verifyArray(
20806f32e7eSjoerg                 Node,
20906f32e7eSjoerg                 [this](msgpack::DocNode &Node) { return verifyInteger(Node); }, 2);
21006f32e7eSjoerg           }))
21106f32e7eSjoerg     return false;
21206f32e7eSjoerg   if (!verifyEntry(KernelMap, ".args", false, [this](msgpack::DocNode &Node) {
21306f32e7eSjoerg         return verifyArray(Node, [this](msgpack::DocNode &Node) {
21406f32e7eSjoerg           return verifyKernelArgs(Node);
21506f32e7eSjoerg         });
21606f32e7eSjoerg       }))
21706f32e7eSjoerg     return false;
21806f32e7eSjoerg   if (!verifyEntry(KernelMap, ".reqd_workgroup_size", false,
21906f32e7eSjoerg                    [this](msgpack::DocNode &Node) {
22006f32e7eSjoerg                      return verifyArray(Node,
22106f32e7eSjoerg                                         [this](msgpack::DocNode &Node) {
22206f32e7eSjoerg                                           return verifyInteger(Node);
22306f32e7eSjoerg                                         },
22406f32e7eSjoerg                                         3);
22506f32e7eSjoerg                    }))
22606f32e7eSjoerg     return false;
22706f32e7eSjoerg   if (!verifyEntry(KernelMap, ".workgroup_size_hint", false,
22806f32e7eSjoerg                    [this](msgpack::DocNode &Node) {
22906f32e7eSjoerg                      return verifyArray(Node,
23006f32e7eSjoerg                                         [this](msgpack::DocNode &Node) {
23106f32e7eSjoerg                                           return verifyInteger(Node);
23206f32e7eSjoerg                                         },
23306f32e7eSjoerg                                         3);
23406f32e7eSjoerg                    }))
23506f32e7eSjoerg     return false;
23606f32e7eSjoerg   if (!verifyScalarEntry(KernelMap, ".vec_type_hint", false,
23706f32e7eSjoerg                          msgpack::Type::String))
23806f32e7eSjoerg     return false;
23906f32e7eSjoerg   if (!verifyScalarEntry(KernelMap, ".device_enqueue_symbol", false,
24006f32e7eSjoerg                          msgpack::Type::String))
24106f32e7eSjoerg     return false;
24206f32e7eSjoerg   if (!verifyIntegerEntry(KernelMap, ".kernarg_segment_size", true))
24306f32e7eSjoerg     return false;
24406f32e7eSjoerg   if (!verifyIntegerEntry(KernelMap, ".group_segment_fixed_size", true))
24506f32e7eSjoerg     return false;
24606f32e7eSjoerg   if (!verifyIntegerEntry(KernelMap, ".private_segment_fixed_size", true))
24706f32e7eSjoerg     return false;
24806f32e7eSjoerg   if (!verifyIntegerEntry(KernelMap, ".kernarg_segment_align", true))
24906f32e7eSjoerg     return false;
25006f32e7eSjoerg   if (!verifyIntegerEntry(KernelMap, ".wavefront_size", true))
25106f32e7eSjoerg     return false;
25206f32e7eSjoerg   if (!verifyIntegerEntry(KernelMap, ".sgpr_count", true))
25306f32e7eSjoerg     return false;
25406f32e7eSjoerg   if (!verifyIntegerEntry(KernelMap, ".vgpr_count", true))
25506f32e7eSjoerg     return false;
25606f32e7eSjoerg   if (!verifyIntegerEntry(KernelMap, ".max_flat_workgroup_size", true))
25706f32e7eSjoerg     return false;
25806f32e7eSjoerg   if (!verifyIntegerEntry(KernelMap, ".sgpr_spill_count", false))
25906f32e7eSjoerg     return false;
26006f32e7eSjoerg   if (!verifyIntegerEntry(KernelMap, ".vgpr_spill_count", false))
26106f32e7eSjoerg     return false;
26206f32e7eSjoerg 
26306f32e7eSjoerg   return true;
26406f32e7eSjoerg }
26506f32e7eSjoerg 
verify(msgpack::DocNode & HSAMetadataRoot)26606f32e7eSjoerg bool MetadataVerifier::verify(msgpack::DocNode &HSAMetadataRoot) {
26706f32e7eSjoerg   if (!HSAMetadataRoot.isMap())
26806f32e7eSjoerg     return false;
26906f32e7eSjoerg   auto &RootMap = HSAMetadataRoot.getMap();
27006f32e7eSjoerg 
27106f32e7eSjoerg   if (!verifyEntry(
27206f32e7eSjoerg           RootMap, "amdhsa.version", true, [this](msgpack::DocNode &Node) {
27306f32e7eSjoerg             return verifyArray(
27406f32e7eSjoerg                 Node,
27506f32e7eSjoerg                 [this](msgpack::DocNode &Node) { return verifyInteger(Node); }, 2);
27606f32e7eSjoerg           }))
27706f32e7eSjoerg     return false;
27806f32e7eSjoerg   if (!verifyEntry(
27906f32e7eSjoerg           RootMap, "amdhsa.printf", false, [this](msgpack::DocNode &Node) {
28006f32e7eSjoerg             return verifyArray(Node, [this](msgpack::DocNode &Node) {
28106f32e7eSjoerg               return verifyScalar(Node, msgpack::Type::String);
28206f32e7eSjoerg             });
28306f32e7eSjoerg           }))
28406f32e7eSjoerg     return false;
28506f32e7eSjoerg   if (!verifyEntry(RootMap, "amdhsa.kernels", true,
28606f32e7eSjoerg                    [this](msgpack::DocNode &Node) {
28706f32e7eSjoerg                      return verifyArray(Node, [this](msgpack::DocNode &Node) {
28806f32e7eSjoerg                        return verifyKernel(Node);
28906f32e7eSjoerg                      });
29006f32e7eSjoerg                    }))
29106f32e7eSjoerg     return false;
29206f32e7eSjoerg 
29306f32e7eSjoerg   return true;
29406f32e7eSjoerg }
29506f32e7eSjoerg 
29606f32e7eSjoerg } // end namespace V3
29706f32e7eSjoerg } // end namespace HSAMD
29806f32e7eSjoerg } // end namespace AMDGPU
29906f32e7eSjoerg } // end namespace llvm
300