Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mesa
Path: blob/21.2-virgl/src/gallium/frontends/clover/nir/invocation.cpp
4573 views
1
//
2
// Copyright 2019 Karol Herbst
3
//
4
// Permission is hereby granted, free of charge, to any person obtaining a
5
// copy of this software and associated documentation files (the "Software"),
6
// to deal in the Software without restriction, including without limitation
7
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
8
// and/or sell copies of the Software, and to permit persons to whom the
9
// Software is furnished to do so, subject to the following conditions:
10
//
11
// The above copyright notice and this permission notice shall be included in
12
// all copies or substantial portions of the Software.
13
//
14
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
18
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20
// OTHER DEALINGS IN THE SOFTWARE.
21
//
22
23
#include "invocation.hpp"
24
25
#include <tuple>
26
27
#include "core/device.hpp"
28
#include "core/error.hpp"
29
#include "core/module.hpp"
30
#include "pipe/p_state.h"
31
#include "util/algorithm.hpp"
32
#include "util/functional.hpp"
33
34
#include <compiler/glsl_types.h>
35
#include <compiler/nir/nir_builder.h>
36
#include <compiler/nir/nir_serialize.h>
37
#include <compiler/spirv/nir_spirv.h>
38
#include <util/u_math.h>
39
40
using namespace clover;
41
42
#ifdef HAVE_CLOVER_SPIRV
43
44
// Refs and unrefs the glsl_type_singleton.
45
static class glsl_type_ref {
46
public:
47
glsl_type_ref() {
48
glsl_type_singleton_init_or_ref();
49
}
50
51
~glsl_type_ref() {
52
glsl_type_singleton_decref();
53
}
54
} glsl_type_ref;
55
56
static const nir_shader_compiler_options *
57
dev_get_nir_compiler_options(const device &dev)
58
{
59
const void *co = dev.get_compiler_options(PIPE_SHADER_IR_NIR);
60
return static_cast<const nir_shader_compiler_options*>(co);
61
}
62
63
static void debug_function(void *private_data,
64
enum nir_spirv_debug_level level, size_t spirv_offset,
65
const char *message)
66
{
67
assert(private_data);
68
auto r_log = reinterpret_cast<std::string *>(private_data);
69
*r_log += message;
70
}
71
72
static void
73
clover_arg_size_align(const glsl_type *type, unsigned *size, unsigned *align)
74
{
75
if (type == glsl_type::sampler_type) {
76
*size = 0;
77
*align = 1;
78
} else if (type->is_image()) {
79
*size = *align = sizeof(cl_mem);
80
} else {
81
*size = type->cl_size();
82
*align = type->cl_alignment();
83
}
84
}
85
86
static bool
87
clover_nir_lower_images(nir_shader *shader)
88
{
89
nir_function_impl *impl = nir_shader_get_entrypoint(shader);
90
91
ASSERTED int last_loc = -1;
92
int num_rd_images = 0, num_wr_images = 0, num_samplers = 0;
93
nir_foreach_uniform_variable(var, shader) {
94
if (glsl_type_is_image(var->type) || glsl_type_is_sampler(var->type)) {
95
/* Assume they come in order */
96
assert(var->data.location > last_loc);
97
last_loc = var->data.location;
98
}
99
100
/* TODO: Constant samplers */
101
if (var->type == glsl_bare_sampler_type()) {
102
var->data.driver_location = num_samplers++;
103
} else if (glsl_type_is_image(var->type)) {
104
if (var->data.access & ACCESS_NON_WRITEABLE)
105
var->data.driver_location = num_rd_images++;
106
else
107
var->data.driver_location = num_wr_images++;
108
} else {
109
/* CL shouldn't have any sampled images */
110
assert(!glsl_type_is_sampler(var->type));
111
}
112
}
113
shader->info.num_textures = num_rd_images;
114
BITSET_ZERO(shader->info.textures_used);
115
if (num_rd_images)
116
BITSET_SET_RANGE(shader->info.textures_used, 0, num_rd_images - 1);
117
shader->info.num_images = num_wr_images;
118
119
nir_builder b;
120
nir_builder_init(&b, impl);
121
122
bool progress = false;
123
nir_foreach_block_reverse(block, impl) {
124
nir_foreach_instr_reverse_safe(instr, block) {
125
switch (instr->type) {
126
case nir_instr_type_deref: {
127
nir_deref_instr *deref = nir_instr_as_deref(instr);
128
if (deref->deref_type != nir_deref_type_var)
129
break;
130
131
if (!glsl_type_is_image(deref->type) &&
132
!glsl_type_is_sampler(deref->type))
133
break;
134
135
b.cursor = nir_instr_remove(&deref->instr);
136
nir_ssa_def *loc =
137
nir_imm_intN_t(&b, deref->var->data.driver_location,
138
deref->dest.ssa.bit_size);
139
nir_ssa_def_rewrite_uses(&deref->dest.ssa, loc);
140
progress = true;
141
break;
142
}
143
144
case nir_instr_type_tex: {
145
nir_tex_instr *tex = nir_instr_as_tex(instr);
146
unsigned count = 0;
147
for (unsigned i = 0; i < tex->num_srcs; i++) {
148
if (tex->src[i].src_type == nir_tex_src_texture_deref ||
149
tex->src[i].src_type == nir_tex_src_sampler_deref) {
150
nir_deref_instr *deref = nir_src_as_deref(tex->src[i].src);
151
if (deref->deref_type == nir_deref_type_var) {
152
/* In this case, we know the actual variable */
153
if (tex->src[i].src_type == nir_tex_src_texture_deref)
154
tex->texture_index = deref->var->data.driver_location;
155
else
156
tex->sampler_index = deref->var->data.driver_location;
157
/* This source gets discarded */
158
nir_instr_rewrite_src(&tex->instr, &tex->src[i].src,
159
NIR_SRC_INIT);
160
continue;
161
} else {
162
assert(tex->src[i].src.is_ssa);
163
b.cursor = nir_before_instr(&tex->instr);
164
/* Back-ends expect a 32-bit thing, not 64-bit */
165
nir_ssa_def *offset = nir_u2u32(&b, tex->src[i].src.ssa);
166
if (tex->src[i].src_type == nir_tex_src_texture_deref)
167
tex->src[count].src_type = nir_tex_src_texture_offset;
168
else
169
tex->src[count].src_type = nir_tex_src_sampler_offset;
170
nir_instr_rewrite_src(&tex->instr, &tex->src[count].src,
171
nir_src_for_ssa(offset));
172
}
173
} else {
174
/* If we've removed a source, move this one down */
175
if (count != i) {
176
assert(count < i);
177
tex->src[count].src_type = tex->src[i].src_type;
178
nir_instr_move_src(&tex->instr, &tex->src[count].src,
179
&tex->src[i].src);
180
}
181
}
182
count++;
183
}
184
tex->num_srcs = count;
185
progress = true;
186
break;
187
}
188
189
case nir_instr_type_intrinsic: {
190
nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
191
switch (intrin->intrinsic) {
192
case nir_intrinsic_image_deref_load:
193
case nir_intrinsic_image_deref_store:
194
case nir_intrinsic_image_deref_atomic_add:
195
case nir_intrinsic_image_deref_atomic_imin:
196
case nir_intrinsic_image_deref_atomic_umin:
197
case nir_intrinsic_image_deref_atomic_imax:
198
case nir_intrinsic_image_deref_atomic_umax:
199
case nir_intrinsic_image_deref_atomic_and:
200
case nir_intrinsic_image_deref_atomic_or:
201
case nir_intrinsic_image_deref_atomic_xor:
202
case nir_intrinsic_image_deref_atomic_exchange:
203
case nir_intrinsic_image_deref_atomic_comp_swap:
204
case nir_intrinsic_image_deref_atomic_fadd:
205
case nir_intrinsic_image_deref_atomic_inc_wrap:
206
case nir_intrinsic_image_deref_atomic_dec_wrap:
207
case nir_intrinsic_image_deref_size:
208
case nir_intrinsic_image_deref_samples: {
209
assert(intrin->src[0].is_ssa);
210
b.cursor = nir_before_instr(&intrin->instr);
211
/* Back-ends expect a 32-bit thing, not 64-bit */
212
nir_ssa_def *offset = nir_u2u32(&b, intrin->src[0].ssa);
213
nir_rewrite_image_intrinsic(intrin, offset, false);
214
progress = true;
215
break;
216
}
217
218
default:
219
break;
220
}
221
break;
222
}
223
224
default:
225
break;
226
}
227
}
228
}
229
230
if (progress) {
231
nir_metadata_preserve(impl, nir_metadata_block_index |
232
nir_metadata_dominance);
233
} else {
234
nir_metadata_preserve(impl, nir_metadata_all);
235
}
236
237
return progress;
238
}
239
240
struct clover_lower_nir_state {
241
std::vector<module::argument> &args;
242
uint32_t global_dims;
243
nir_variable *constant_var;
244
nir_variable *printf_buffer;
245
nir_variable *offset_vars[3];
246
};
247
248
static bool
249
clover_lower_nir_filter(const nir_instr *instr, const void *)
250
{
251
return instr->type == nir_instr_type_intrinsic;
252
}
253
254
static nir_ssa_def *
255
clover_lower_nir_instr(nir_builder *b, nir_instr *instr, void *_state)
256
{
257
clover_lower_nir_state *state = reinterpret_cast<clover_lower_nir_state*>(_state);
258
nir_intrinsic_instr *intrinsic = nir_instr_as_intrinsic(instr);
259
260
switch (intrinsic->intrinsic) {
261
case nir_intrinsic_load_printf_buffer_address: {
262
if (!state->printf_buffer) {
263
unsigned location = state->args.size();
264
state->args.emplace_back(module::argument::global, sizeof(size_t),
265
8, 8, module::argument::zero_ext,
266
module::argument::printf_buffer);
267
268
const glsl_type *type = glsl_uint64_t_type();
269
state->printf_buffer = nir_variable_create(b->shader, nir_var_uniform,
270
type, "global_printf_buffer");
271
state->printf_buffer->data.location = location;
272
}
273
return nir_load_var(b, state->printf_buffer);
274
}
275
case nir_intrinsic_load_base_global_invocation_id: {
276
nir_ssa_def *loads[3];
277
278
/* create variables if we didn't do so alrady */
279
if (!state->offset_vars[0]) {
280
/* TODO: fix for 64 bit */
281
/* Even though we only place one scalar argument, clover will bind up to
282
* three 32 bit values
283
*/
284
unsigned location = state->args.size();
285
state->args.emplace_back(module::argument::scalar, 4, 4, 4,
286
module::argument::zero_ext,
287
module::argument::grid_offset);
288
289
const glsl_type *type = glsl_uint_type();
290
for (uint32_t i = 0; i < 3; i++) {
291
state->offset_vars[i] =
292
nir_variable_create(b->shader, nir_var_uniform, type,
293
"global_invocation_id_offsets");
294
state->offset_vars[i]->data.location = location + i;
295
}
296
}
297
298
for (int i = 0; i < 3; i++) {
299
nir_variable *var = state->offset_vars[i];
300
loads[i] = var ? nir_load_var(b, var) : nir_imm_int(b, 0);
301
}
302
303
return nir_u2u(b, nir_vec(b, loads, state->global_dims),
304
nir_dest_bit_size(intrinsic->dest));
305
}
306
case nir_intrinsic_load_constant_base_ptr: {
307
return nir_load_var(b, state->constant_var);
308
}
309
310
default:
311
return NULL;
312
}
313
}
314
315
static bool
316
clover_lower_nir(nir_shader *nir, std::vector<module::argument> &args,
317
uint32_t dims, uint32_t pointer_bit_size)
318
{
319
nir_variable *constant_var = NULL;
320
if (nir->constant_data_size) {
321
const glsl_type *type = pointer_bit_size == 64 ? glsl_uint64_t_type() : glsl_uint_type();
322
323
constant_var = nir_variable_create(nir, nir_var_uniform, type,
324
"constant_buffer_addr");
325
constant_var->data.location = args.size();
326
327
args.emplace_back(module::argument::global, sizeof(cl_mem),
328
pointer_bit_size / 8, pointer_bit_size / 8,
329
module::argument::zero_ext,
330
module::argument::constant_buffer);
331
}
332
333
clover_lower_nir_state state = { args, dims, constant_var };
334
return nir_shader_lower_instructions(nir,
335
clover_lower_nir_filter, clover_lower_nir_instr, &state);
336
}
337
338
static spirv_to_nir_options
339
create_spirv_options(const device &dev, std::string &r_log)
340
{
341
struct spirv_to_nir_options spirv_options = {};
342
spirv_options.environment = NIR_SPIRV_OPENCL;
343
if (dev.address_bits() == 32u) {
344
spirv_options.shared_addr_format = nir_address_format_32bit_offset;
345
spirv_options.global_addr_format = nir_address_format_32bit_global;
346
spirv_options.temp_addr_format = nir_address_format_32bit_offset;
347
spirv_options.constant_addr_format = nir_address_format_32bit_global;
348
} else {
349
spirv_options.shared_addr_format = nir_address_format_32bit_offset_as_64bit;
350
spirv_options.global_addr_format = nir_address_format_64bit_global;
351
spirv_options.temp_addr_format = nir_address_format_32bit_offset_as_64bit;
352
spirv_options.constant_addr_format = nir_address_format_64bit_global;
353
}
354
spirv_options.caps.address = true;
355
spirv_options.caps.float64 = true;
356
spirv_options.caps.int8 = true;
357
spirv_options.caps.int16 = true;
358
spirv_options.caps.int64 = true;
359
spirv_options.caps.kernel = true;
360
spirv_options.caps.kernel_image = dev.image_support();
361
spirv_options.caps.int64_atomics = dev.has_int64_atomics();
362
spirv_options.debug.func = &debug_function;
363
spirv_options.debug.private_data = &r_log;
364
spirv_options.caps.printf = true;
365
return spirv_options;
366
}
367
368
struct disk_cache *clover::nir::create_clc_disk_cache(void)
369
{
370
struct mesa_sha1 ctx;
371
unsigned char sha1[20];
372
char cache_id[20 * 2 + 1];
373
_mesa_sha1_init(&ctx);
374
375
if (!disk_cache_get_function_identifier((void *)clover::nir::create_clc_disk_cache, &ctx))
376
return NULL;
377
378
_mesa_sha1_final(&ctx, sha1);
379
380
disk_cache_format_hex_id(cache_id, sha1, 20 * 2);
381
return disk_cache_create("clover-clc", cache_id, 0);
382
}
383
384
void clover::nir::check_for_libclc(const device &dev)
385
{
386
if (!nir_can_find_libclc(dev.address_bits()))
387
throw error(CL_COMPILER_NOT_AVAILABLE);
388
}
389
390
nir_shader *clover::nir::load_libclc_nir(const device &dev, std::string &r_log)
391
{
392
spirv_to_nir_options spirv_options = create_spirv_options(dev, r_log);
393
auto *compiler_options = dev_get_nir_compiler_options(dev);
394
395
return nir_load_libclc_shader(dev.address_bits(), dev.clc_cache,
396
&spirv_options, compiler_options);
397
}
398
399
module clover::nir::spirv_to_nir(const module &mod, const device &dev,
400
std::string &r_log)
401
{
402
spirv_to_nir_options spirv_options = create_spirv_options(dev, r_log);
403
std::shared_ptr<nir_shader> nir = dev.clc_nir;
404
spirv_options.clc_shader = nir.get();
405
406
module m;
407
// We only insert one section.
408
assert(mod.secs.size() == 1);
409
auto &section = mod.secs[0];
410
411
module::resource_id section_id = 0;
412
for (const auto &sym : mod.syms) {
413
assert(sym.section == 0);
414
415
const auto *binary =
416
reinterpret_cast<const pipe_binary_program_header *>(section.data.data());
417
const uint32_t *data = reinterpret_cast<const uint32_t *>(binary->blob);
418
const size_t num_words = binary->num_bytes / 4;
419
const char *name = sym.name.c_str();
420
auto *compiler_options = dev_get_nir_compiler_options(dev);
421
422
nir_shader *nir = spirv_to_nir(data, num_words, nullptr, 0,
423
MESA_SHADER_KERNEL, name,
424
&spirv_options, compiler_options);
425
if (!nir) {
426
r_log += "Translation from SPIR-V to NIR for kernel \"" + sym.name +
427
"\" failed.\n";
428
throw build_error();
429
}
430
431
nir->info.workgroup_size_variable = sym.reqd_work_group_size[0] == 0;
432
nir->info.workgroup_size[0] = sym.reqd_work_group_size[0];
433
nir->info.workgroup_size[1] = sym.reqd_work_group_size[1];
434
nir->info.workgroup_size[2] = sym.reqd_work_group_size[2];
435
nir_validate_shader(nir, "clover");
436
437
// Inline all functions first.
438
// according to the comment on nir_inline_functions
439
NIR_PASS_V(nir, nir_lower_variable_initializers, nir_var_function_temp);
440
NIR_PASS_V(nir, nir_lower_returns);
441
NIR_PASS_V(nir, nir_lower_libclc, spirv_options.clc_shader);
442
443
NIR_PASS_V(nir, nir_inline_functions);
444
NIR_PASS_V(nir, nir_copy_prop);
445
NIR_PASS_V(nir, nir_opt_deref);
446
447
// Pick off the single entrypoint that we want.
448
foreach_list_typed_safe(nir_function, func, node, &nir->functions) {
449
if (!func->is_entrypoint)
450
exec_node_remove(&func->node);
451
}
452
assert(exec_list_length(&nir->functions) == 1);
453
454
nir_validate_shader(nir, "clover after function inlining");
455
456
NIR_PASS_V(nir, nir_lower_variable_initializers, ~nir_var_function_temp);
457
458
struct nir_lower_printf_options printf_options;
459
printf_options.treat_doubles_as_floats = false;
460
printf_options.max_buffer_size = dev.max_printf_buffer_size();
461
462
NIR_PASS_V(nir, nir_lower_printf, &printf_options);
463
464
NIR_PASS_V(nir, nir_remove_dead_variables, nir_var_function_temp, NULL);
465
466
// copy propagate to prepare for lower_explicit_io
467
NIR_PASS_V(nir, nir_split_var_copies);
468
NIR_PASS_V(nir, nir_opt_copy_prop_vars);
469
NIR_PASS_V(nir, nir_lower_var_copies);
470
NIR_PASS_V(nir, nir_lower_vars_to_ssa);
471
NIR_PASS_V(nir, nir_opt_dce);
472
NIR_PASS_V(nir, nir_lower_convert_alu_types, NULL);
473
474
NIR_PASS_V(nir, nir_lower_system_values);
475
nir_lower_compute_system_values_options sysval_options = { 0 };
476
sysval_options.has_base_global_invocation_id = true;
477
NIR_PASS_V(nir, nir_lower_compute_system_values, &sysval_options);
478
479
// constant fold before lowering mem constants
480
NIR_PASS_V(nir, nir_opt_constant_folding);
481
482
NIR_PASS_V(nir, nir_remove_dead_variables, nir_var_mem_constant, NULL);
483
NIR_PASS_V(nir, nir_lower_vars_to_explicit_types, nir_var_mem_constant,
484
glsl_get_cl_type_size_align);
485
if (nir->constant_data_size > 0) {
486
assert(nir->constant_data == NULL);
487
nir->constant_data = rzalloc_size(nir, nir->constant_data_size);
488
nir_gather_explicit_io_initializers(nir, nir->constant_data,
489
nir->constant_data_size,
490
nir_var_mem_constant);
491
}
492
NIR_PASS_V(nir, nir_lower_explicit_io, nir_var_mem_constant,
493
spirv_options.constant_addr_format);
494
495
auto args = sym.args;
496
NIR_PASS_V(nir, clover_lower_nir, args, dev.max_block_size().size(),
497
dev.address_bits());
498
499
NIR_PASS_V(nir, nir_lower_vars_to_explicit_types,
500
nir_var_uniform, clover_arg_size_align);
501
NIR_PASS_V(nir, nir_lower_vars_to_explicit_types,
502
nir_var_mem_shared | nir_var_mem_global |
503
nir_var_function_temp,
504
glsl_get_cl_type_size_align);
505
506
NIR_PASS_V(nir, nir_opt_deref);
507
NIR_PASS_V(nir, nir_lower_readonly_images_to_tex, false);
508
NIR_PASS_V(nir, clover_nir_lower_images);
509
NIR_PASS_V(nir, nir_lower_memcpy);
510
511
/* use offsets for kernel inputs (uniform) */
512
NIR_PASS_V(nir, nir_lower_explicit_io, nir_var_uniform,
513
nir->info.cs.ptr_size == 64 ?
514
nir_address_format_32bit_offset_as_64bit :
515
nir_address_format_32bit_offset);
516
517
NIR_PASS_V(nir, nir_lower_explicit_io, nir_var_mem_constant,
518
spirv_options.constant_addr_format);
519
NIR_PASS_V(nir, nir_lower_explicit_io, nir_var_mem_shared,
520
spirv_options.shared_addr_format);
521
522
NIR_PASS_V(nir, nir_lower_explicit_io, nir_var_function_temp,
523
spirv_options.temp_addr_format);
524
525
NIR_PASS_V(nir, nir_lower_explicit_io, nir_var_mem_global,
526
spirv_options.global_addr_format);
527
528
NIR_PASS_V(nir, nir_remove_dead_variables, nir_var_all, NULL);
529
530
if (compiler_options->lower_int64_options)
531
NIR_PASS_V(nir, nir_lower_int64);
532
533
NIR_PASS_V(nir, nir_opt_dce);
534
535
if (nir->constant_data_size) {
536
const char *ptr = reinterpret_cast<const char *>(nir->constant_data);
537
const module::section constants {
538
section_id,
539
module::section::data_constant,
540
nir->constant_data_size,
541
{ ptr, ptr + nir->constant_data_size }
542
};
543
nir->constant_data = NULL;
544
nir->constant_data_size = 0;
545
m.secs.push_back(constants);
546
}
547
548
void *mem_ctx = ralloc_context(NULL);
549
unsigned printf_info_count = nir->printf_info_count;
550
nir_printf_info *printf_infos = nir->printf_info;
551
552
ralloc_steal(mem_ctx, printf_infos);
553
554
struct blob blob;
555
blob_init(&blob);
556
nir_serialize(&blob, nir, false);
557
558
ralloc_free(nir);
559
560
const pipe_binary_program_header header { uint32_t(blob.size) };
561
module::section text { section_id, module::section::text_executable, header.num_bytes, {} };
562
text.data.insert(text.data.end(), reinterpret_cast<const char *>(&header),
563
reinterpret_cast<const char *>(&header) + sizeof(header));
564
text.data.insert(text.data.end(), blob.data, blob.data + blob.size);
565
566
free(blob.data);
567
568
m.printf_strings_in_buffer = false;
569
m.printf_infos.reserve(printf_info_count);
570
for (unsigned i = 0; i < printf_info_count; i++) {
571
module::printf_info info;
572
573
info.arg_sizes.reserve(printf_infos[i].num_args);
574
for (unsigned j = 0; j < printf_infos[i].num_args; j++)
575
info.arg_sizes.push_back(printf_infos[i].arg_sizes[j]);
576
577
info.strings.resize(printf_infos[i].string_size);
578
memcpy(info.strings.data(), printf_infos[i].strings, printf_infos[i].string_size);
579
m.printf_infos.push_back(info);
580
}
581
582
ralloc_free(mem_ctx);
583
584
m.syms.emplace_back(sym.name, std::string(),
585
sym.reqd_work_group_size, section_id, 0, args);
586
m.secs.push_back(text);
587
section_id++;
588
}
589
return m;
590
}
591
#else
592
module clover::nir::spirv_to_nir(const module &mod, const device &dev, std::string &r_log)
593
{
594
r_log += "SPIR-V support in clover is not enabled.\n";
595
throw error(CL_LINKER_NOT_AVAILABLE);
596
}
597
#endif
598
599