1*0b57cec5SDimitry Andric //===- AMDGPUMetadataVerifier.cpp - MsgPack Types ---------------*- C++ -*-===//
2*0b57cec5SDimitry Andric //
3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0b57cec5SDimitry Andric //
7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
8*0b57cec5SDimitry Andric //
9*0b57cec5SDimitry Andric /// \file
10*0b57cec5SDimitry Andric /// Implements a verifier for AMDGPU HSA metadata.
11*0b57cec5SDimitry Andric //
12*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
13*0b57cec5SDimitry Andric 
14*0b57cec5SDimitry Andric #include "llvm/BinaryFormat/AMDGPUMetadataVerifier.h"
15*0b57cec5SDimitry Andric #include "llvm/ADT/StringSwitch.h"
16*0b57cec5SDimitry Andric #include "llvm/Support/AMDGPUMetadata.h"
17*0b57cec5SDimitry Andric 
18*0b57cec5SDimitry Andric namespace llvm {
19*0b57cec5SDimitry Andric namespace AMDGPU {
20*0b57cec5SDimitry Andric namespace HSAMD {
21*0b57cec5SDimitry Andric namespace V3 {
22*0b57cec5SDimitry Andric 
verifyScalar(msgpack::DocNode & Node,msgpack::Type SKind,function_ref<bool (msgpack::DocNode &)> verifyValue)23*0b57cec5SDimitry Andric bool MetadataVerifier::verifyScalar(
24*0b57cec5SDimitry Andric     msgpack::DocNode &Node, msgpack::Type SKind,
25*0b57cec5SDimitry Andric     function_ref<bool(msgpack::DocNode &)> verifyValue) {
26*0b57cec5SDimitry Andric   if (!Node.isScalar())
27*0b57cec5SDimitry Andric     return false;
28*0b57cec5SDimitry Andric   if (Node.getKind() != SKind) {
29*0b57cec5SDimitry Andric     if (Strict)
30*0b57cec5SDimitry Andric       return false;
31*0b57cec5SDimitry Andric     // If we are not strict, we interpret string values as "implicitly typed"
32*0b57cec5SDimitry Andric     // and attempt to coerce them to the expected type here.
33*0b57cec5SDimitry Andric     if (Node.getKind() != msgpack::Type::String)
34*0b57cec5SDimitry Andric       return false;
35*0b57cec5SDimitry Andric     StringRef StringValue = Node.getString();
36*0b57cec5SDimitry Andric     Node.fromString(StringValue);
37*0b57cec5SDimitry Andric     if (Node.getKind() != SKind)
38*0b57cec5SDimitry Andric       return false;
39*0b57cec5SDimitry Andric   }
40*0b57cec5SDimitry Andric   if (verifyValue)
41*0b57cec5SDimitry Andric     return verifyValue(Node);
42*0b57cec5SDimitry Andric   return true;
43*0b57cec5SDimitry Andric }
44*0b57cec5SDimitry Andric 
verifyInteger(msgpack::DocNode & Node)45*0b57cec5SDimitry Andric bool MetadataVerifier::verifyInteger(msgpack::DocNode &Node) {
46*0b57cec5SDimitry Andric   if (!verifyScalar(Node, msgpack::Type::UInt))
47*0b57cec5SDimitry Andric     if (!verifyScalar(Node, msgpack::Type::Int))
48*0b57cec5SDimitry Andric       return false;
49*0b57cec5SDimitry Andric   return true;
50*0b57cec5SDimitry Andric }
51*0b57cec5SDimitry Andric 
verifyArray(msgpack::DocNode & Node,function_ref<bool (msgpack::DocNode &)> verifyNode,Optional<size_t> Size)52*0b57cec5SDimitry Andric bool MetadataVerifier::verifyArray(
53*0b57cec5SDimitry Andric     msgpack::DocNode &Node, function_ref<bool(msgpack::DocNode &)> verifyNode,
54*0b57cec5SDimitry Andric     Optional<size_t> Size) {
55*0b57cec5SDimitry Andric   if (!Node.isArray())
56*0b57cec5SDimitry Andric     return false;
57*0b57cec5SDimitry Andric   auto &Array = Node.getArray();
58*0b57cec5SDimitry Andric   if (Size && Array.size() != *Size)
59*0b57cec5SDimitry Andric     return false;
60*0b57cec5SDimitry Andric   for (auto &Item : Array)
61*0b57cec5SDimitry Andric     if (!verifyNode(Item))
62*0b57cec5SDimitry Andric       return false;
63*0b57cec5SDimitry Andric 
64*0b57cec5SDimitry Andric   return true;
65*0b57cec5SDimitry Andric }
66*0b57cec5SDimitry Andric 
verifyEntry(msgpack::MapDocNode & MapNode,StringRef Key,bool Required,function_ref<bool (msgpack::DocNode &)> verifyNode)67*0b57cec5SDimitry Andric bool MetadataVerifier::verifyEntry(
68*0b57cec5SDimitry Andric     msgpack::MapDocNode &MapNode, StringRef Key, bool Required,
69*0b57cec5SDimitry Andric     function_ref<bool(msgpack::DocNode &)> verifyNode) {
70*0b57cec5SDimitry Andric   auto Entry = MapNode.find(Key);
71*0b57cec5SDimitry Andric   if (Entry == MapNode.end())
72*0b57cec5SDimitry Andric     return !Required;
73*0b57cec5SDimitry Andric   return verifyNode(Entry->second);
74*0b57cec5SDimitry Andric }
75*0b57cec5SDimitry Andric 
verifyScalarEntry(msgpack::MapDocNode & MapNode,StringRef Key,bool Required,msgpack::Type SKind,function_ref<bool (msgpack::DocNode &)> verifyValue)76*0b57cec5SDimitry Andric bool MetadataVerifier::verifyScalarEntry(
77*0b57cec5SDimitry Andric     msgpack::MapDocNode &MapNode, StringRef Key, bool Required,
78*0b57cec5SDimitry Andric     msgpack::Type SKind,
79*0b57cec5SDimitry Andric     function_ref<bool(msgpack::DocNode &)> verifyValue) {
80*0b57cec5SDimitry Andric   return verifyEntry(MapNode, Key, Required, [=](msgpack::DocNode &Node) {
81*0b57cec5SDimitry Andric     return verifyScalar(Node, SKind, verifyValue);
82*0b57cec5SDimitry Andric   });
83*0b57cec5SDimitry Andric }
84*0b57cec5SDimitry Andric 
verifyIntegerEntry(msgpack::MapDocNode & MapNode,StringRef Key,bool Required)85*0b57cec5SDimitry Andric bool MetadataVerifier::verifyIntegerEntry(msgpack::MapDocNode &MapNode,
86*0b57cec5SDimitry Andric                                           StringRef Key, bool Required) {
87*0b57cec5SDimitry Andric   return verifyEntry(MapNode, Key, Required, [this](msgpack::DocNode &Node) {
88*0b57cec5SDimitry Andric     return verifyInteger(Node);
89*0b57cec5SDimitry Andric   });
90*0b57cec5SDimitry Andric }
91*0b57cec5SDimitry Andric 
verifyKernelArgs(msgpack::DocNode & Node)92*0b57cec5SDimitry Andric bool MetadataVerifier::verifyKernelArgs(msgpack::DocNode &Node) {
93*0b57cec5SDimitry Andric   if (!Node.isMap())
94*0b57cec5SDimitry Andric     return false;
95*0b57cec5SDimitry Andric   auto &ArgsMap = Node.getMap();
96*0b57cec5SDimitry Andric 
97*0b57cec5SDimitry Andric   if (!verifyScalarEntry(ArgsMap, ".name", false,
98*0b57cec5SDimitry Andric                          msgpack::Type::String))
99*0b57cec5SDimitry Andric     return false;
100*0b57cec5SDimitry Andric   if (!verifyScalarEntry(ArgsMap, ".type_name", false,
101*0b57cec5SDimitry Andric                          msgpack::Type::String))
102*0b57cec5SDimitry Andric     return false;
103*0b57cec5SDimitry Andric   if (!verifyIntegerEntry(ArgsMap, ".size", true))
104*0b57cec5SDimitry Andric     return false;
105*0b57cec5SDimitry Andric   if (!verifyIntegerEntry(ArgsMap, ".offset", true))
106*0b57cec5SDimitry Andric     return false;
107*0b57cec5SDimitry Andric   if (!verifyScalarEntry(ArgsMap, ".value_kind", true,
108*0b57cec5SDimitry Andric                          msgpack::Type::String,
109*0b57cec5SDimitry Andric                          [](msgpack::DocNode &SNode) {
110*0b57cec5SDimitry Andric                            return StringSwitch<bool>(SNode.getString())
111*0b57cec5SDimitry Andric                                .Case("by_value", true)
112*0b57cec5SDimitry Andric                                .Case("global_buffer", true)
113*0b57cec5SDimitry Andric                                .Case("dynamic_shared_pointer", true)
114*0b57cec5SDimitry Andric                                .Case("sampler", true)
115*0b57cec5SDimitry Andric                                .Case("image", true)
116*0b57cec5SDimitry Andric                                .Case("pipe", true)
117*0b57cec5SDimitry Andric                                .Case("queue", true)
118*0b57cec5SDimitry Andric                                .Case("hidden_global_offset_x", true)
119*0b57cec5SDimitry Andric                                .Case("hidden_global_offset_y", true)
120*0b57cec5SDimitry Andric                                .Case("hidden_global_offset_z", true)
121*0b57cec5SDimitry Andric                                .Case("hidden_none", true)
122*0b57cec5SDimitry Andric                                .Case("hidden_printf_buffer", true)
123*0b57cec5SDimitry Andric                                .Case("hidden_hostcall_buffer", true)
124*0b57cec5SDimitry Andric                                .Case("hidden_default_queue", true)
125*0b57cec5SDimitry Andric                                .Case("hidden_completion_action", true)
126*0b57cec5SDimitry Andric                                .Case("hidden_multigrid_sync_arg", true)
127*0b57cec5SDimitry Andric                                .Default(false);
128*0b57cec5SDimitry Andric                          }))
129*0b57cec5SDimitry Andric     return false;
130*0b57cec5SDimitry Andric   if (!verifyIntegerEntry(ArgsMap, ".pointee_align", false))
131*0b57cec5SDimitry Andric     return false;
132*0b57cec5SDimitry Andric   if (!verifyScalarEntry(ArgsMap, ".address_space", false,
133*0b57cec5SDimitry Andric                          msgpack::Type::String,
134*0b57cec5SDimitry Andric                          [](msgpack::DocNode &SNode) {
135*0b57cec5SDimitry Andric                            return StringSwitch<bool>(SNode.getString())
136*0b57cec5SDimitry Andric                                .Case("private", true)
137*0b57cec5SDimitry Andric                                .Case("global", true)
138*0b57cec5SDimitry Andric                                .Case("constant", true)
139*0b57cec5SDimitry Andric                                .Case("local", true)
140*0b57cec5SDimitry Andric                                .Case("generic", true)
141*0b57cec5SDimitry Andric                                .Case("region", true)
142*0b57cec5SDimitry Andric                                .Default(false);
143*0b57cec5SDimitry Andric                          }))
144*0b57cec5SDimitry Andric     return false;
145*0b57cec5SDimitry Andric   if (!verifyScalarEntry(ArgsMap, ".access", false,
146*0b57cec5SDimitry Andric                          msgpack::Type::String,
147*0b57cec5SDimitry Andric                          [](msgpack::DocNode &SNode) {
148*0b57cec5SDimitry Andric                            return StringSwitch<bool>(SNode.getString())
149*0b57cec5SDimitry Andric                                .Case("read_only", true)
150*0b57cec5SDimitry Andric                                .Case("write_only", true)
151*0b57cec5SDimitry Andric                                .Case("read_write", true)
152*0b57cec5SDimitry Andric                                .Default(false);
153*0b57cec5SDimitry Andric                          }))
154*0b57cec5SDimitry Andric     return false;
155*0b57cec5SDimitry Andric   if (!verifyScalarEntry(ArgsMap, ".actual_access", false,
156*0b57cec5SDimitry Andric                          msgpack::Type::String,
157*0b57cec5SDimitry Andric                          [](msgpack::DocNode &SNode) {
158*0b57cec5SDimitry Andric                            return StringSwitch<bool>(SNode.getString())
159*0b57cec5SDimitry Andric                                .Case("read_only", true)
160*0b57cec5SDimitry Andric                                .Case("write_only", true)
161*0b57cec5SDimitry Andric                                .Case("read_write", true)
162*0b57cec5SDimitry Andric                                .Default(false);
163*0b57cec5SDimitry Andric                          }))
164*0b57cec5SDimitry Andric     return false;
165*0b57cec5SDimitry Andric   if (!verifyScalarEntry(ArgsMap, ".is_const", false,
166*0b57cec5SDimitry Andric                          msgpack::Type::Boolean))
167*0b57cec5SDimitry Andric     return false;
168*0b57cec5SDimitry Andric   if (!verifyScalarEntry(ArgsMap, ".is_restrict", false,
169*0b57cec5SDimitry Andric                          msgpack::Type::Boolean))
170*0b57cec5SDimitry Andric     return false;
171*0b57cec5SDimitry Andric   if (!verifyScalarEntry(ArgsMap, ".is_volatile", false,
172*0b57cec5SDimitry Andric                          msgpack::Type::Boolean))
173*0b57cec5SDimitry Andric     return false;
174*0b57cec5SDimitry Andric   if (!verifyScalarEntry(ArgsMap, ".is_pipe", false,
175*0b57cec5SDimitry Andric                          msgpack::Type::Boolean))
176*0b57cec5SDimitry Andric     return false;
177*0b57cec5SDimitry Andric 
178*0b57cec5SDimitry Andric   return true;
179*0b57cec5SDimitry Andric }
180*0b57cec5SDimitry Andric 
verifyKernel(msgpack::DocNode & Node)181*0b57cec5SDimitry Andric bool MetadataVerifier::verifyKernel(msgpack::DocNode &Node) {
182*0b57cec5SDimitry Andric   if (!Node.isMap())
183*0b57cec5SDimitry Andric     return false;
184*0b57cec5SDimitry Andric   auto &KernelMap = Node.getMap();
185*0b57cec5SDimitry Andric 
186*0b57cec5SDimitry Andric   if (!verifyScalarEntry(KernelMap, ".name", true,
187*0b57cec5SDimitry Andric                          msgpack::Type::String))
188*0b57cec5SDimitry Andric     return false;
189*0b57cec5SDimitry Andric   if (!verifyScalarEntry(KernelMap, ".symbol", true,
190*0b57cec5SDimitry Andric                          msgpack::Type::String))
191*0b57cec5SDimitry Andric     return false;
192*0b57cec5SDimitry Andric   if (!verifyScalarEntry(KernelMap, ".language", false,
193*0b57cec5SDimitry Andric                          msgpack::Type::String,
194*0b57cec5SDimitry Andric                          [](msgpack::DocNode &SNode) {
195*0b57cec5SDimitry Andric                            return StringSwitch<bool>(SNode.getString())
196*0b57cec5SDimitry Andric                                .Case("OpenCL C", true)
197*0b57cec5SDimitry Andric                                .Case("OpenCL C++", true)
198*0b57cec5SDimitry Andric                                .Case("HCC", true)
199*0b57cec5SDimitry Andric                                .Case("HIP", true)
200*0b57cec5SDimitry Andric                                .Case("OpenMP", true)
201*0b57cec5SDimitry Andric                                .Case("Assembler", true)
202*0b57cec5SDimitry Andric                                .Default(false);
203*0b57cec5SDimitry Andric                          }))
204*0b57cec5SDimitry Andric     return false;
205*0b57cec5SDimitry Andric   if (!verifyEntry(
206*0b57cec5SDimitry Andric           KernelMap, ".language_version", false, [this](msgpack::DocNode &Node) {
207*0b57cec5SDimitry Andric             return verifyArray(
208*0b57cec5SDimitry Andric                 Node,
209*0b57cec5SDimitry Andric                 [this](msgpack::DocNode &Node) { return verifyInteger(Node); }, 2);
210*0b57cec5SDimitry Andric           }))
211*0b57cec5SDimitry Andric     return false;
212*0b57cec5SDimitry Andric   if (!verifyEntry(KernelMap, ".args", false, [this](msgpack::DocNode &Node) {
213*0b57cec5SDimitry Andric         return verifyArray(Node, [this](msgpack::DocNode &Node) {
214*0b57cec5SDimitry Andric           return verifyKernelArgs(Node);
215*0b57cec5SDimitry Andric         });
216*0b57cec5SDimitry Andric       }))
217*0b57cec5SDimitry Andric     return false;
218*0b57cec5SDimitry Andric   if (!verifyEntry(KernelMap, ".reqd_workgroup_size", false,
219*0b57cec5SDimitry Andric                    [this](msgpack::DocNode &Node) {
220*0b57cec5SDimitry Andric                      return verifyArray(Node,
221*0b57cec5SDimitry Andric                                         [this](msgpack::DocNode &Node) {
222*0b57cec5SDimitry Andric                                           return verifyInteger(Node);
223*0b57cec5SDimitry Andric                                         },
224*0b57cec5SDimitry Andric                                         3);
225*0b57cec5SDimitry Andric                    }))
226*0b57cec5SDimitry Andric     return false;
227*0b57cec5SDimitry Andric   if (!verifyEntry(KernelMap, ".workgroup_size_hint", false,
228*0b57cec5SDimitry Andric                    [this](msgpack::DocNode &Node) {
229*0b57cec5SDimitry Andric                      return verifyArray(Node,
230*0b57cec5SDimitry Andric                                         [this](msgpack::DocNode &Node) {
231*0b57cec5SDimitry Andric                                           return verifyInteger(Node);
232*0b57cec5SDimitry Andric                                         },
233*0b57cec5SDimitry Andric                                         3);
234*0b57cec5SDimitry Andric                    }))
235*0b57cec5SDimitry Andric     return false;
236*0b57cec5SDimitry Andric   if (!verifyScalarEntry(KernelMap, ".vec_type_hint", false,
237*0b57cec5SDimitry Andric                          msgpack::Type::String))
238*0b57cec5SDimitry Andric     return false;
239*0b57cec5SDimitry Andric   if (!verifyScalarEntry(KernelMap, ".device_enqueue_symbol", false,
240*0b57cec5SDimitry Andric                          msgpack::Type::String))
241*0b57cec5SDimitry Andric     return false;
242*0b57cec5SDimitry Andric   if (!verifyIntegerEntry(KernelMap, ".kernarg_segment_size", true))
243*0b57cec5SDimitry Andric     return false;
244*0b57cec5SDimitry Andric   if (!verifyIntegerEntry(KernelMap, ".group_segment_fixed_size", true))
245*0b57cec5SDimitry Andric     return false;
246*0b57cec5SDimitry Andric   if (!verifyIntegerEntry(KernelMap, ".private_segment_fixed_size", true))
247*0b57cec5SDimitry Andric     return false;
248*0b57cec5SDimitry Andric   if (!verifyIntegerEntry(KernelMap, ".kernarg_segment_align", true))
249*0b57cec5SDimitry Andric     return false;
250*0b57cec5SDimitry Andric   if (!verifyIntegerEntry(KernelMap, ".wavefront_size", true))
251*0b57cec5SDimitry Andric     return false;
252*0b57cec5SDimitry Andric   if (!verifyIntegerEntry(KernelMap, ".sgpr_count", true))
253*0b57cec5SDimitry Andric     return false;
254*0b57cec5SDimitry Andric   if (!verifyIntegerEntry(KernelMap, ".vgpr_count", true))
255*0b57cec5SDimitry Andric     return false;
256*0b57cec5SDimitry Andric   if (!verifyIntegerEntry(KernelMap, ".max_flat_workgroup_size", true))
257*0b57cec5SDimitry Andric     return false;
258*0b57cec5SDimitry Andric   if (!verifyIntegerEntry(KernelMap, ".sgpr_spill_count", false))
259*0b57cec5SDimitry Andric     return false;
260*0b57cec5SDimitry Andric   if (!verifyIntegerEntry(KernelMap, ".vgpr_spill_count", false))
261*0b57cec5SDimitry Andric     return false;
262*0b57cec5SDimitry Andric 
263*0b57cec5SDimitry Andric   return true;
264*0b57cec5SDimitry Andric }
265*0b57cec5SDimitry Andric 
verify(msgpack::DocNode & HSAMetadataRoot)266*0b57cec5SDimitry Andric bool MetadataVerifier::verify(msgpack::DocNode &HSAMetadataRoot) {
267*0b57cec5SDimitry Andric   if (!HSAMetadataRoot.isMap())
268*0b57cec5SDimitry Andric     return false;
269*0b57cec5SDimitry Andric   auto &RootMap = HSAMetadataRoot.getMap();
270*0b57cec5SDimitry Andric 
271*0b57cec5SDimitry Andric   if (!verifyEntry(
272*0b57cec5SDimitry Andric           RootMap, "amdhsa.version", true, [this](msgpack::DocNode &Node) {
273*0b57cec5SDimitry Andric             return verifyArray(
274*0b57cec5SDimitry Andric                 Node,
275*0b57cec5SDimitry Andric                 [this](msgpack::DocNode &Node) { return verifyInteger(Node); }, 2);
276*0b57cec5SDimitry Andric           }))
277*0b57cec5SDimitry Andric     return false;
278*0b57cec5SDimitry Andric   if (!verifyEntry(
279*0b57cec5SDimitry Andric           RootMap, "amdhsa.printf", false, [this](msgpack::DocNode &Node) {
280*0b57cec5SDimitry Andric             return verifyArray(Node, [this](msgpack::DocNode &Node) {
281*0b57cec5SDimitry Andric               return verifyScalar(Node, msgpack::Type::String);
282*0b57cec5SDimitry Andric             });
283*0b57cec5SDimitry Andric           }))
284*0b57cec5SDimitry Andric     return false;
285*0b57cec5SDimitry Andric   if (!verifyEntry(RootMap, "amdhsa.kernels", true,
286*0b57cec5SDimitry Andric                    [this](msgpack::DocNode &Node) {
287*0b57cec5SDimitry Andric                      return verifyArray(Node, [this](msgpack::DocNode &Node) {
288*0b57cec5SDimitry Andric                        return verifyKernel(Node);
289*0b57cec5SDimitry Andric                      });
290*0b57cec5SDimitry Andric                    }))
291*0b57cec5SDimitry Andric     return false;
292*0b57cec5SDimitry Andric 
293*0b57cec5SDimitry Andric   return true;
294*0b57cec5SDimitry Andric }
295*0b57cec5SDimitry Andric 
296*0b57cec5SDimitry Andric } // end namespace V3
297*0b57cec5SDimitry Andric } // end namespace HSAMD
298*0b57cec5SDimitry Andric } // end namespace AMDGPU
299*0b57cec5SDimitry Andric } // end namespace llvm
300*0b57cec5SDimitry Andric