diff --git a/meson.build b/meson.build index a1728c4ac8e00..22d49a5a40098 100644 --- a/meson.build +++ b/meson.build @@ -78,7 +78,6 @@ if with_tools.contains('all') 'asahi', ] endif -with_clc = false with_any_vulkan_layers = get_option('vulkan-layers').length() != 0 with_intel_tools = with_tools.contains('intel') or with_tools.contains('intel-ui') @@ -308,19 +307,9 @@ if with_aco_tests and not with_amd_vk error('ACO tests require Radv') endif -dep_clang = dependency( - 'clang', - method : 'cmake', - static : true, - modules : [ - 'clangBasic', 'clangCodeGen', 'clangDriver', 'clangFrontend', 'clangFrontendTool', - 'clangHandleCXX', 'clangHandleLLVM', - ], - required : get_option('microsoft-clc'), -) -with_microsoft_clc = dep_clang.found() -with_clc = dep_clang.found() - +with_microsoft_clc = get_option('microsoft-clc').enabled() +with_clc = with_microsoft_clc +with_libclc = with_clc with_spirv_to_dxil = get_option('spirv-to-dxil') if host_machine.system() == 'darwin' @@ -880,7 +869,7 @@ if _opencl != 'disabled' error('OpenCL Clover implementation requires at least one gallium driver.') endif - with_clc = true + with_libclc = true with_gallium_opencl = true with_opencl_icd = _opencl == 'icd' else @@ -889,7 +878,7 @@ else endif dep_clc = null_dep -if with_clc +if with_libclc dep_clc = dependency('libclc') endif @@ -1607,8 +1596,8 @@ if with_gallium_opencl ] llvm_optional_modules += ['frontendopenmp'] endif -if with_microsoft_clc - llvm_modules += ['target', 'linker', 'irreader', 'option', 'libdriver'] +if with_clc + llvm_modules += ['coverage', 'target', 'linker', 'irreader', 'option', 'libdriver', 'lto'] endif if with_tests or with_gallium_softpipe llvm_modules += 'native' @@ -1616,7 +1605,7 @@ endif if with_amd_vk or with_gallium_radeonsi _llvm_version = '>= 11.0.0' -elif with_microsoft_clc +elif with_clc _llvm_version = '>= 10.0.0' elif with_gallium_opencl _llvm_version = '>= 8.0.0' @@ -1669,7 +1658,7 @@ if _llvm != 'disabled' optional_modules : llvm_optional_modules, required : ( with_amd_vk or with_gallium_radeonsi or with_gallium_swr or - with_gallium_opencl or with_microsoft_clc or _llvm == 'enabled' + with_gallium_opencl or with_clc or _llvm == 'enabled' ), static : not _shared_llvm, method : _llvm_method, @@ -1726,13 +1715,13 @@ elif with_amd_vk or with_gallium_radeonsi or with_gallium_swr or with_swrast_vk error('The following drivers require LLVM: Radv, RadeonSI, SWR, Lavapipe. One of these is enabled, but LLVM is disabled.') elif with_gallium_opencl error('The OpenCL "Clover" state tracker requires LLVM, but LLVM is disabled.') -elif with_microsoft_clc - error('The Microsoft CLC compiler requires LLVM, but LLVM is disabled.') +elif with_clc + error('The CLC compiler requires LLVM, but LLVM is disabled.') else draw_with_llvm = false endif -with_opencl_spirv = (_opencl != 'disabled' and get_option('opencl-spirv')) or with_microsoft_clc +with_opencl_spirv = (_opencl != 'disabled' and get_option('opencl-spirv')) or with_clc if with_opencl_spirv chosen_llvm_version_array = dep_llvm.version().split('.') chosen_llvm_version_major = chosen_llvm_version_array[0].to_int() @@ -1757,6 +1746,23 @@ else dep_llvmspirvlib = null_dep endif +dep_clang = null_dep +if with_clc + llvm_libdir = dep_llvm.get_variable(cmake : 'LLVM_LIBRARY_DIR', configtool: 'libdir') + + clang_modules = [ + 'clangBasic', 'clangAST', 'clangCodeGen', 'clangLex', + 'clangDriver', 'clangFrontend', 'clangFrontendTool', + 'clangHandleCXX', 'clangHandleLLVM', 'clangSerialization', + 'clangSema', 'clangParse', 'clangEdit', 'clangAnalysis' + ] + + dep_clang = [] + foreach m : clang_modules + dep_clang += cpp.find_library(m, dirs : llvm_libdir, required : true) + endforeach +endif + # Be explicit about only using this lib on Windows, to avoid picking # up random libs with the generic name 'libversion' dep_version = null_dep diff --git a/src/compiler/clc/clc.c b/src/compiler/clc/clc.c new file mode 100644 index 0000000000000..3d4e9ca857d87 --- /dev/null +++ b/src/compiler/clc/clc.c @@ -0,0 +1,314 @@ +/* + * Copyright © Microsoft Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "nir/nir.h" +#include "nir/nir_serialize.h" +#include "glsl_types.h" +#include "nir_types.h" +#include "clc.h" +#include "clc_helpers.h" +#include "spirv/nir_spirv.h" +#include "util/u_debug.h" + +#include + +enum clc_debug_flags { + CLC_DEBUG_DUMP_SPIRV = 1 << 0, + CLC_DEBUG_VERBOSE = 1 << 1, +}; + +static const struct debug_named_value clc_debug_options[] = { + { "dump_spirv", CLC_DEBUG_DUMP_SPIRV, "Dump spirv blobs" }, + { "verbose", CLC_DEBUG_VERBOSE, NULL }, + DEBUG_NAMED_VALUE_END +}; + +DEBUG_GET_ONCE_FLAGS_OPTION(debug_clc, "CLC_DEBUG", clc_debug_options, 0) + +static void +clc_print_kernels_info(const struct clc_parsed_spirv *obj) +{ + fprintf(stdout, "Kernels:\n"); + for (unsigned i = 0; i < obj->num_kernels; i++) { + const struct clc_kernel_arg *args = obj->kernels[i].args; + bool first = true; + + fprintf(stdout, "\tvoid %s(", obj->kernels[i].name); + for (unsigned j = 0; j < obj->kernels[i].num_args; j++) { + if (!first) + fprintf(stdout, ", "); + else + first = false; + + switch (args[j].address_qualifier) { + case CLC_KERNEL_ARG_ADDRESS_GLOBAL: + fprintf(stdout, "__global "); + break; + case CLC_KERNEL_ARG_ADDRESS_LOCAL: + fprintf(stdout, "__local "); + break; + case CLC_KERNEL_ARG_ADDRESS_CONSTANT: + fprintf(stdout, "__constant "); + break; + default: + break; + } + + if (args[j].type_qualifier & CLC_KERNEL_ARG_TYPE_VOLATILE) + fprintf(stdout, "volatile "); + if (args[j].type_qualifier & CLC_KERNEL_ARG_TYPE_CONST) + fprintf(stdout, "const "); + if (args[j].type_qualifier & CLC_KERNEL_ARG_TYPE_RESTRICT) + fprintf(stdout, "restrict "); + + fprintf(stdout, "%s %s", args[j].type_name, args[j].name); + } + fprintf(stdout, ");\n"); + } +} + +static void +clc_libclc_optimize(nir_shader *s) +{ + bool progress; + do { + progress = false; + NIR_PASS(progress, s, nir_split_var_copies); + NIR_PASS(progress, s, nir_opt_copy_prop_vars); + NIR_PASS(progress, s, nir_lower_var_copies); + NIR_PASS(progress, s, nir_lower_vars_to_ssa); + NIR_PASS(progress, s, nir_copy_prop); + NIR_PASS(progress, s, nir_opt_remove_phis); + NIR_PASS(progress, s, nir_opt_dce); + NIR_PASS(progress, s, nir_opt_if, true); + NIR_PASS(progress, s, nir_opt_dead_cf); + NIR_PASS(progress, s, nir_opt_cse); + NIR_PASS(progress, s, nir_opt_peephole_select, 8, true, true); + NIR_PASS(progress, s, nir_opt_algebraic); + NIR_PASS(progress, s, nir_opt_constant_folding); + NIR_PASS(progress, s, nir_opt_undef); + NIR_PASS(progress, s, nir_lower_undef_to_zero); + NIR_PASS(progress, s, nir_opt_deref); + } while (progress); +} + +struct clc_libclc { + const nir_shader *libclc_nir; +}; + +struct clc_libclc * +clc_libclc_new(const struct clc_logger *logger, const struct clc_libclc_options *options) +{ + struct clc_libclc *ctx = rzalloc(NULL, struct clc_libclc); + if (!ctx) { + clc_error(logger, "D3D12: failed to allocate a clc_libclc"); + return NULL; + } + + const struct spirv_to_nir_options libclc_spirv_options = { + .environment = NIR_SPIRV_OPENCL, + .create_library = true, + .constant_addr_format = nir_address_format_32bit_index_offset_pack64, + .global_addr_format = nir_address_format_32bit_index_offset_pack64, + .shared_addr_format = nir_address_format_32bit_offset_as_64bit, + .temp_addr_format = nir_address_format_32bit_offset_as_64bit, + .float_controls_execution_mode = FLOAT_CONTROLS_DENORM_FLUSH_TO_ZERO_FP32, + .caps = { + .address = true, + .float64 = true, + .int8 = true, + .int16 = true, + .int64 = true, + .kernel = true, + }, + }; + + glsl_type_singleton_init_or_ref(); + nir_shader *s = nir_load_libclc_shader(64, NULL, &libclc_spirv_options, options->nir_options); + if (!s) { + clc_error(logger, "D3D12: spirv_to_nir failed on libclc blob"); + ralloc_free(ctx); + return NULL; + } + + if (options && options->optimize) + clc_libclc_optimize(s); + + ralloc_steal(ctx, s); + ctx->libclc_nir = s; + + return ctx; +} + +void clc_free_libclc(struct clc_libclc *ctx) +{ + ralloc_free(ctx); + glsl_type_singleton_decref(); +} + +const nir_shader *clc_libclc_get_clc_shader(struct clc_libclc *ctx) +{ + return ctx->libclc_nir; +} + +void clc_libclc_serialize(struct clc_libclc *context, + void **serialized, + size_t *serialized_size) +{ + struct blob tmp; + blob_init(&tmp); + nir_serialize(&tmp, context->libclc_nir, true); + + blob_finish_get_buffer(&tmp, serialized, serialized_size); +} + +void clc_libclc_free_serialized(void *serialized) +{ + free(serialized); +} + +struct clc_libclc * +clc_libclc_deserialize(const void *serialized, size_t serialized_size) +{ + struct clc_libclc *ctx = rzalloc(NULL, struct clc_libclc); + if (!ctx) { + return NULL; + } + + glsl_type_singleton_init_or_ref(); + + struct blob_reader tmp; + blob_reader_init(&tmp, serialized, serialized_size); + + nir_shader *s = nir_deserialize(NULL, NULL, &tmp); + if (!s) { + ralloc_free(ctx); + return NULL; + } + + ralloc_steal(ctx, s); + ctx->libclc_nir = s; + + return ctx; +} + +bool +clc_compile_c_to_spir(const struct clc_compile_args *args, + const struct clc_logger *logger, + struct clc_binary *out_spir) +{ + return clc_c_to_spir(args, logger, out_spir) >= 0; +} + +void +clc_free_spir(struct clc_binary *spir) +{ + clc_free_spir_binary(spir); +} + +bool +clc_compile_spir_to_spirv(const struct clc_binary *in_spir, + const struct clc_logger *logger, + struct clc_binary *out_spirv) +{ + if (clc_spir_to_spirv(in_spir, logger, out_spirv) < 0) + return false; + + if (debug_get_option_debug_clc() & CLC_DEBUG_DUMP_SPIRV) + clc_dump_spirv(out_spirv, stdout); + + return true; +} + +void +clc_free_spirv(struct clc_binary *spirv) +{ + clc_free_spirv_binary(spirv); +} + +bool +clc_compile_c_to_spirv(const struct clc_compile_args *args, + const struct clc_logger *logger, + struct clc_binary *out_spirv) +{ + if (clc_c_to_spirv(args, logger, out_spirv) < 0) + return false; + + if (debug_get_option_debug_clc() & CLC_DEBUG_DUMP_SPIRV) + clc_dump_spirv(out_spirv, stdout); + + return true; +} + +bool +clc_link_spirv(const struct clc_linker_args *args, + const struct clc_logger *logger, + struct clc_binary *out_spirv) +{ + if (clc_link_spirv_binaries(args, logger, out_spirv) < 0) + return false; + + if (debug_get_option_debug_clc() & CLC_DEBUG_DUMP_SPIRV) + clc_dump_spirv(out_spirv, stdout); + + return true; +} + +bool +clc_parse_spirv(const struct clc_binary *in_spirv, + const struct clc_logger *logger, + struct clc_parsed_spirv *out_data) +{ + if (!clc_spirv_get_kernels_info(in_spirv, + &out_data->kernels, + &out_data->num_kernels, + &out_data->spec_constants, + &out_data->num_spec_constants, + logger)) + return false; + + if (debug_get_option_debug_clc() & CLC_DEBUG_VERBOSE) + clc_print_kernels_info(out_data); + + return true; +} + +void clc_free_parsed_spirv(struct clc_parsed_spirv *data) +{ + clc_free_kernels_info(data->kernels, data->num_kernels); +} + +bool +clc_specialize_spirv(const struct clc_binary *in_spirv, + const struct clc_parsed_spirv *parsed_data, + const struct clc_spirv_specialization_consts *consts, + struct clc_binary *out_spirv) +{ + if (!clc_spirv_specialize(in_spirv, parsed_data, consts, out_spirv)) + return false; + + if (debug_get_option_debug_clc() & CLC_DEBUG_DUMP_SPIRV) + clc_dump_spirv(out_spirv, stdout); + + return true; +} diff --git a/src/compiler/clc/clc.h b/src/compiler/clc/clc.h new file mode 100644 index 0000000000000..7ce661c9a680b --- /dev/null +++ b/src/compiler/clc/clc.h @@ -0,0 +1,229 @@ +/* + * Copyright © Microsoft Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef MESA_CLC_H +#define MESA_CLC_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct nir_shader nir_shader; +struct nir_shader_compiler_options; + +struct clc_named_value { + const char *name; + const char *value; +}; + +struct clc_compile_args { + const struct clc_named_value *headers; + unsigned num_headers; + struct clc_named_value source; + const char * const *args; + unsigned num_args; +}; + +struct clc_binary { + void *data; + size_t size; +}; + +struct clc_linker_args { + const struct clc_binary * const *in_objs; + unsigned num_in_objs; + unsigned create_library; +}; + +typedef void (*clc_msg_callback)(void *priv, const char *msg); + +struct clc_logger { + void *priv; + clc_msg_callback error; + clc_msg_callback warning; +}; + +enum clc_kernel_arg_type_qualifier { + CLC_KERNEL_ARG_TYPE_CONST = 1 << 0, + CLC_KERNEL_ARG_TYPE_RESTRICT = 1 << 1, + CLC_KERNEL_ARG_TYPE_VOLATILE = 1 << 2, +}; + +enum clc_kernel_arg_access_qualifier { + CLC_KERNEL_ARG_ACCESS_READ = 1 << 0, + CLC_KERNEL_ARG_ACCESS_WRITE = 1 << 1, +}; + +enum clc_kernel_arg_address_qualifier { + CLC_KERNEL_ARG_ADDRESS_PRIVATE, + CLC_KERNEL_ARG_ADDRESS_CONSTANT, + CLC_KERNEL_ARG_ADDRESS_LOCAL, + CLC_KERNEL_ARG_ADDRESS_GLOBAL, +}; + +struct clc_kernel_arg { + const char *name; + const char *type_name; + unsigned type_qualifier; + unsigned access_qualifier; + enum clc_kernel_arg_address_qualifier address_qualifier; +}; + +enum clc_vec_hint_type { + CLC_VEC_HINT_TYPE_CHAR = 0, + CLC_VEC_HINT_TYPE_SHORT = 1, + CLC_VEC_HINT_TYPE_INT = 2, + CLC_VEC_HINT_TYPE_LONG = 3, + CLC_VEC_HINT_TYPE_HALF = 4, + CLC_VEC_HINT_TYPE_FLOAT = 5, + CLC_VEC_HINT_TYPE_DOUBLE = 6 +}; + +struct clc_kernel_info { + const char *name; + size_t num_args; + const struct clc_kernel_arg *args; + + unsigned vec_hint_size; + enum clc_vec_hint_type vec_hint_type; +}; + +enum clc_spec_constant_type { + CLC_SPEC_CONSTANT_UNKNOWN, + CLC_SPEC_CONSTANT_BOOL, + CLC_SPEC_CONSTANT_FLOAT, + CLC_SPEC_CONSTANT_DOUBLE, + CLC_SPEC_CONSTANT_INT8, + CLC_SPEC_CONSTANT_UINT8, + CLC_SPEC_CONSTANT_INT16, + CLC_SPEC_CONSTANT_UINT16, + CLC_SPEC_CONSTANT_INT32, + CLC_SPEC_CONSTANT_UINT32, + CLC_SPEC_CONSTANT_INT64, + CLC_SPEC_CONSTANT_UINT64, +}; + +struct clc_parsed_spec_constant { + uint32_t id; + enum clc_spec_constant_type type; +}; + +struct clc_parsed_spirv { + const struct clc_kernel_info *kernels; + unsigned num_kernels; + + const struct clc_parsed_spec_constant *spec_constants; + unsigned num_spec_constants; +}; + +struct clc_libclc; + +struct clc_libclc_options { + unsigned optimize; + const struct nir_shader_compiler_options *nir_options; +}; + +struct clc_libclc *clc_libclc_new(const struct clc_logger *logger, const struct clc_libclc_options *options); + +void clc_free_libclc(struct clc_libclc *lib); + +const nir_shader *clc_libclc_get_clc_shader(struct clc_libclc *lib); + +void clc_libclc_serialize(struct clc_libclc *lib, void **serialized, size_t *size); +void clc_libclc_free_serialized(void *serialized); +struct clc_libclc *clc_libclc_deserialize(const void *serialized, size_t size); + +bool +clc_compile_c_to_spir(const struct clc_compile_args *args, + const struct clc_logger *logger, + struct clc_binary *out_spir); + +void +clc_free_spir(struct clc_binary *spir); + +bool +clc_compile_spir_to_spirv(const struct clc_binary *in_spir, + const struct clc_logger *logger, + struct clc_binary *out_spirv); + +void +clc_free_spirv(struct clc_binary *spirv); + +bool +clc_compile_c_to_spirv(const struct clc_compile_args *args, + const struct clc_logger *logger, + struct clc_binary *out_spirv); + +bool +clc_link_spirv(const struct clc_linker_args *args, + const struct clc_logger *logger, + struct clc_binary *out_spirv); + +bool +clc_parse_spirv(const struct clc_binary *in_spirv, + const struct clc_logger *logger, + struct clc_parsed_spirv *out_data); + +void +clc_free_parsed_spirv(struct clc_parsed_spirv *data); + +typedef union { + bool b; + float f32; + double f64; + int8_t i8; + uint8_t u8; + int16_t i16; + uint16_t u16; + int32_t i32; + uint32_t u32; + int64_t i64; + uint64_t u64; +} clc_spirv_const_value; + +struct clc_spirv_specialization { + uint32_t id; + clc_spirv_const_value value; + bool defined_on_module; +}; + +struct clc_spirv_specialization_consts { + const struct clc_spirv_specialization *specializations; + unsigned num_specializations; +}; + +bool +clc_specialize_spirv(const struct clc_binary *in_spirv, + const struct clc_parsed_spirv *parsed_data, + const struct clc_spirv_specialization_consts *consts, + struct clc_binary *out_spirv); + +#ifdef __cplusplus +} +#endif + +#endif /* MESA_CLC_H */ diff --git a/src/microsoft/clc/clc_helpers.cpp b/src/compiler/clc/clc_helpers.cpp similarity index 99% rename from src/microsoft/clc/clc_helpers.cpp rename to src/compiler/clc/clc_helpers.cpp index 0ca560ffafe23..60dc57158d52d 100644 --- a/src/microsoft/clc/clc_helpers.cpp +++ b/src/compiler/clc/clc_helpers.cpp @@ -59,6 +59,8 @@ #include "opencl-c.h.h" #include "opencl-c-base.h.h" +#include "clc_helpers.h" + constexpr spv_target_env spirv_target = SPV_ENV_UNIVERSAL_1_0; using ::llvm::Function; diff --git a/src/microsoft/clc/clc_helpers.h b/src/compiler/clc/clc_helpers.h similarity index 97% rename from src/microsoft/clc/clc_helpers.h rename to src/compiler/clc/clc_helpers.h index 6edf261c3bc06..cbad142efeb90 100644 --- a/src/microsoft/clc/clc_helpers.h +++ b/src/compiler/clc/clc_helpers.h @@ -21,16 +21,12 @@ * IN THE SOFTWARE. */ -#ifndef CLC_TO_NIR_H -#define CLC_TO_NIR_H - -#ifdef __cplusplus -extern "C" { -#endif +#ifndef MESA_CLC_HELPERS_H +#define MESA_CLC_HELPERS_H #include "nir_types.h" -#include "clc_compiler.h" +#include "clc.h" #include "util/u_string.h" #include @@ -38,6 +34,10 @@ extern "C" { #include #include +#ifdef __cplusplus +extern "C" { +#endif + bool clc_spirv_get_kernels_info(const struct clc_binary *spvbin, const struct clc_kernel_info **kernels, @@ -101,4 +101,4 @@ clc_free_spirv_binary(struct clc_binary *spvbin); } #endif -#endif +#endif /* MESA_CLC_HELPERS_H */ diff --git a/src/compiler/clc/meson.build b/src/compiler/clc/meson.build new file mode 100644 index 0000000000000..cc4687b4c8c0a --- /dev/null +++ b/src/compiler/clc/meson.build @@ -0,0 +1,56 @@ +# Copyright © Microsoft Corporation + +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice (including the next +# paragraph) shall be included in all copies or substantial portions of the +# Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +clang_resource_dir = join_paths(llvm_libdir, 'clang', dep_llvm.version(), 'include') + +opencl_c_h = custom_target( + 'opencl-c.h', + input : [files_xxd, join_paths(clang_resource_dir, 'opencl-c.h')], + output : 'opencl-c.h.h', + command : [prog_python, '@INPUT@', '@OUTPUT@', '-n', 'opencl_c_source'], +) +opencl_c_base_h = custom_target( + 'opencl-c-base.h', + input : [files_xxd, join_paths(clang_resource_dir, 'opencl-c-base.h')], + output : 'opencl-c-base.h.h', + command : [prog_python, '@INPUT@', '@OUTPUT@', '-n', 'opencl_c_base_source'], +) + +files_libclc = files( + 'clc.c', + 'clc_helpers.cpp', +) + +_libclc = static_library( + 'libclc', + files_libclc, + opencl_c_h, + opencl_c_base_h, + include_directories : [inc_include, inc_src, inc_mapi, inc_mesa, inc_compiler, inc_spirv], + cpp_args : ['-DCLANG_RESOURCE_DIR="@0@"'.format(clang_resource_dir)], + dependencies: [idep_nir_headers, dep_clang, dep_llvm, dep_llvmspirvlib, + idep_mesautil, idep_nir, dep_spirv_tools] +) + +idep_clc = declare_dependency( + link_with : _libclc, + include_directories : include_directories('.'), +) diff --git a/src/compiler/meson.build b/src/compiler/meson.build index 9cb55b3f46063..0f3ec6a8b2a40 100644 --- a/src/compiler/meson.build +++ b/src/compiler/meson.build @@ -97,5 +97,8 @@ if with_tests ) endif +if with_clc + subdir('clc') +endif subdir('glsl') subdir('isaspec') diff --git a/src/microsoft/clc/clc_compiler.c b/src/microsoft/clc/clc_compiler.c index 93bde0afb9875..9927065d4326c 100644 --- a/src/microsoft/clc/clc_compiler.c +++ b/src/microsoft/clc/clc_compiler.c @@ -40,61 +40,6 @@ #include "git_sha1.h" -enum clc_debug_flags { - CLC_DEBUG_DUMP_SPIRV = 1 << 0, - CLC_DEBUG_VERBOSE = 1 << 1, -}; - -static const struct debug_named_value clc_debug_options[] = { - { "dump_spirv", CLC_DEBUG_DUMP_SPIRV, "Dump spirv blobs" }, - { "verbose", CLC_DEBUG_VERBOSE, NULL }, - DEBUG_NAMED_VALUE_END -}; - -DEBUG_GET_ONCE_FLAGS_OPTION(debug_clc, "CLC_DEBUG", clc_debug_options, 0) - -static void -clc_print_kernels_info(const struct clc_parsed_spirv *obj) -{ - fprintf(stdout, "Kernels:\n"); - for (unsigned i = 0; i < obj->num_kernels; i++) { - const struct clc_kernel_arg *args = obj->kernels[i].args; - bool first = true; - - fprintf(stdout, "\tvoid %s(", obj->kernels[i].name); - for (unsigned j = 0; j < obj->kernels[i].num_args; j++) { - if (!first) - fprintf(stdout, ", "); - else - first = false; - - switch (args[j].address_qualifier) { - case CLC_KERNEL_ARG_ADDRESS_GLOBAL: - fprintf(stdout, "__global "); - break; - case CLC_KERNEL_ARG_ADDRESS_LOCAL: - fprintf(stdout, "__local "); - break; - case CLC_KERNEL_ARG_ADDRESS_CONSTANT: - fprintf(stdout, "__constant "); - break; - default: - break; - } - - if (args[j].type_qualifier & CLC_KERNEL_ARG_TYPE_VOLATILE) - fprintf(stdout, "volatile "); - if (args[j].type_qualifier & CLC_KERNEL_ARG_TYPE_CONST) - fprintf(stdout, "const "); - if (args[j].type_qualifier & CLC_KERNEL_ARG_TYPE_RESTRICT) - fprintf(stdout, "restrict "); - - fprintf(stdout, "%s %s", args[j].type_name, args[j].name); - } - fprintf(stdout, ");\n"); - } -} - struct clc_image_lower_context { struct clc_dxil_metadata *metadata; @@ -449,233 +394,6 @@ clc_lower_nonnormalized_samplers(nir_shader *nir, } } - -static void -clc_libclc_optimize(nir_shader *s) -{ - bool progress; - do { - progress = false; - NIR_PASS(progress, s, nir_split_var_copies); - NIR_PASS(progress, s, nir_opt_copy_prop_vars); - NIR_PASS(progress, s, nir_lower_var_copies); - NIR_PASS(progress, s, nir_lower_vars_to_ssa); - NIR_PASS(progress, s, nir_copy_prop); - NIR_PASS(progress, s, nir_opt_remove_phis); - NIR_PASS(progress, s, nir_opt_dce); - NIR_PASS(progress, s, nir_opt_if, true); - NIR_PASS(progress, s, nir_opt_dead_cf); - NIR_PASS(progress, s, nir_opt_cse); - NIR_PASS(progress, s, nir_opt_peephole_select, 8, true, true); - NIR_PASS(progress, s, nir_opt_algebraic); - NIR_PASS(progress, s, nir_opt_constant_folding); - NIR_PASS(progress, s, nir_opt_undef); - NIR_PASS(progress, s, nir_lower_undef_to_zero); - NIR_PASS(progress, s, nir_opt_deref); - } while (progress); -} - -struct clc_libclc { - const void *libclc_nir; -}; - -struct clc_libclc * -clc_libclc_new(const struct clc_logger *logger, const struct clc_libclc_options *options) -{ - struct clc_libclc *ctx = rzalloc(NULL, struct clc_libclc); - if (!ctx) { - clc_error(logger, "D3D12: failed to allocate a clc_libclc"); - return NULL; - } - - const struct spirv_to_nir_options libclc_spirv_options = { - .environment = NIR_SPIRV_OPENCL, - .create_library = true, - .constant_addr_format = nir_address_format_32bit_index_offset_pack64, - .global_addr_format = nir_address_format_32bit_index_offset_pack64, - .shared_addr_format = nir_address_format_32bit_offset_as_64bit, - .temp_addr_format = nir_address_format_32bit_offset_as_64bit, - .float_controls_execution_mode = FLOAT_CONTROLS_DENORM_FLUSH_TO_ZERO_FP32, - .caps = { - .address = true, - .float64 = true, - .int8 = true, - .int16 = true, - .int64 = true, - .kernel = true, - }, - }; - const struct nir_shader_compiler_options *libclc_nir_options = - dxil_get_nir_compiler_options(); - - glsl_type_singleton_init_or_ref(); - nir_shader *s = nir_load_libclc_shader(64, NULL, &libclc_spirv_options, libclc_nir_options); - if (!s) { - clc_error(logger, "D3D12: spirv_to_nir failed on libclc blob"); - ralloc_free(ctx); - return NULL; - } - - if (options && options->optimize) - clc_libclc_optimize(s); - - ralloc_steal(ctx, s); - ctx->libclc_nir = s; - - return ctx; -} - -void -clc_free_libclc(struct clc_libclc *ctx) -{ - ralloc_free(ctx); - glsl_type_singleton_decref(); -}; - -void clc_libclc_serialize(struct clc_libclc *context, - void **serialized, - size_t *serialized_size) -{ - struct blob tmp; - blob_init(&tmp); - nir_serialize(&tmp, context->libclc_nir, true); - - blob_finish_get_buffer(&tmp, serialized, serialized_size); -} - -void clc_libclc_free_serialized(void *serialized) -{ - free(serialized); -} - -struct clc_libclc * - clc_libclc_deserialize(const void *serialized, size_t serialized_size) -{ - struct clc_libclc *ctx = rzalloc(NULL, struct clc_libclc); - if (!ctx) { - return NULL; - } - const struct nir_shader_compiler_options *libclc_nir_options = - dxil_get_nir_compiler_options(); - - glsl_type_singleton_init_or_ref(); - - struct blob_reader tmp; - blob_reader_init(&tmp, serialized, serialized_size); - - nir_shader *s = nir_deserialize(NULL, libclc_nir_options, &tmp); - if (!s) { - ralloc_free(ctx); - return NULL; - } - - ralloc_steal(ctx, s); - ctx->libclc_nir = s; - - return ctx; -} - -bool -clc_compile_c_to_spir(const struct clc_compile_args *args, - const struct clc_logger *logger, - struct clc_binary *out_spir) -{ - return clc_c_to_spir(args, logger, out_spir) >= 0; -} - -void -clc_free_spir(struct clc_binary *spir) -{ - clc_free_spir_binary(spir); -} - -bool -clc_compile_spir_to_spirv(const struct clc_binary *in_spir, - const struct clc_logger *logger, - struct clc_binary *out_spirv) -{ - if (clc_spir_to_spirv(in_spir, logger, out_spirv) < 0) - return false; - - if (debug_get_option_debug_clc() & CLC_DEBUG_DUMP_SPIRV) - clc_dump_spirv(out_spirv, stdout); - - return true; -} - -void -clc_free_spirv(struct clc_binary *spirv) -{ - clc_free_spirv_binary(spirv); -} - -bool -clc_compile_c_to_spirv(const struct clc_compile_args *args, - const struct clc_logger *logger, - struct clc_binary *out_spirv) -{ - if (clc_c_to_spirv(args, logger, out_spirv) < 0) - return false; - - if (debug_get_option_debug_clc() & CLC_DEBUG_DUMP_SPIRV) - clc_dump_spirv(out_spirv, stdout); - - return true; -} - -bool -clc_link_spirv(const struct clc_linker_args *args, - const struct clc_logger *logger, - struct clc_binary *out_spirv) -{ - if (clc_link_spirv_binaries(args, logger, out_spirv) < 0) - return false; - - if (debug_get_option_debug_clc() & CLC_DEBUG_DUMP_SPIRV) - clc_dump_spirv(out_spirv, stdout); - - return true; -} - -bool -clc_parse_spirv(const struct clc_binary *in_spirv, - const struct clc_logger *logger, - struct clc_parsed_spirv *out_data) -{ - if (!clc_spirv_get_kernels_info(in_spirv, - &out_data->kernels, - &out_data->num_kernels, - &out_data->spec_constants, - &out_data->num_spec_constants, - logger)) - return false; - - if (debug_get_option_debug_clc() & CLC_DEBUG_VERBOSE) - clc_print_kernels_info(out_data); - - return true; -} - -void clc_free_parsed_spirv(struct clc_parsed_spirv *data) -{ - clc_free_kernels_info(data->kernels, data->num_kernels); -} - -bool -clc_specialize_spirv(const struct clc_binary *in_spirv, - const struct clc_parsed_spirv *parsed_data, - const struct clc_spirv_specialization_consts *consts, - struct clc_binary *out_spirv) -{ - if (!clc_spirv_specialize(in_spirv, parsed_data, consts, out_spirv)) - return false; - - if (debug_get_option_debug_clc() & CLC_DEBUG_DUMP_SPIRV) - clc_dump_spirv(out_spirv, stdout); - - return true; -} - static nir_variable * add_kernel_inputs_var(struct clc_dxil_object *dxil, nir_shader *nir, unsigned *cbv_id) @@ -928,7 +646,7 @@ split_unaligned_loads_stores(nir_shader *shader) unsigned alignment = align_offset ? 1 << (ffs(align_offset) - 1) : align_mul; - /* We can load anything at 4-byte alignment, except for + /* We can load anything at 4-byte alignment, except for * UBOs (AKA CBs where the granularity is 16 bytes). */ if (alignment >= (deref->modes == nir_var_mem_ubo ? 16 : 4)) @@ -1049,6 +767,18 @@ scale_fdiv(nir_shader *nir) return progress; } +struct clc_libclc * +clc_libclc_new_dxil(const struct clc_logger *logger, + const struct clc_libclc_dxil_options *options) +{ + struct clc_libclc_options clc_options = { + .optimize = options->optimize, + .nir_options = dxil_get_nir_compiler_options(), + }; + + return clc_libclc_new(logger, &clc_options); +} + bool clc_spirv_to_dxil(struct clc_libclc *lib, const struct clc_binary *linked_spirv, @@ -1075,7 +805,7 @@ clc_spirv_to_dxil(struct clc_libclc *lib, const struct spirv_to_nir_options spirv_options = { .environment = NIR_SPIRV_OPENCL, - .clc_shader = lib->libclc_nir, + .clc_shader = clc_libclc_get_clc_shader(lib), .constant_addr_format = nir_address_format_32bit_index_offset_pack64, .global_addr_format = nir_address_format_32bit_index_offset_pack64, .shared_addr_format = nir_address_format_32bit_offset_as_64bit, @@ -1153,7 +883,7 @@ clc_spirv_to_dxil(struct clc_libclc *lib, // according to the comment on nir_inline_functions NIR_PASS_V(nir, nir_lower_variable_initializers, nir_var_function_temp); NIR_PASS_V(nir, nir_lower_returns); - NIR_PASS_V(nir, nir_lower_libclc, lib->libclc_nir); + NIR_PASS_V(nir, nir_lower_libclc, clc_libclc_get_clc_shader(lib)); NIR_PASS_V(nir, nir_inline_functions); // Pick off the single entrypoint that we want. diff --git a/src/microsoft/clc/clc_compiler.h b/src/microsoft/clc/clc_compiler.h index 44b41d6ac9f2a..9fcbfd4334e2e 100644 --- a/src/microsoft/clc/clc_compiler.h +++ b/src/microsoft/clc/clc_compiler.h @@ -24,118 +24,12 @@ #ifndef CLC_COMPILER_H #define CLC_COMPILER_H +#include "clc/clc.h" + #ifdef __cplusplus extern "C" { #endif -#include -#include - -struct clc_named_value { - const char *name; - const char *value; -}; - -struct clc_compile_args { - const struct clc_named_value *headers; - unsigned num_headers; - struct clc_named_value source; - const char * const *args; - unsigned num_args; -}; - -struct clc_binary { - void *data; - size_t size; -}; - -struct clc_linker_args { - const struct clc_binary * const *in_objs; - unsigned num_in_objs; - unsigned create_library; -}; - -typedef void (*clc_msg_callback)(void *priv, const char *msg); - -struct clc_logger { - void *priv; - clc_msg_callback error; - clc_msg_callback warning; -}; - -enum clc_kernel_arg_type_qualifier { - CLC_KERNEL_ARG_TYPE_CONST = 1 << 0, - CLC_KERNEL_ARG_TYPE_RESTRICT = 1 << 1, - CLC_KERNEL_ARG_TYPE_VOLATILE = 1 << 2, -}; - -enum clc_kernel_arg_access_qualifier { - CLC_KERNEL_ARG_ACCESS_READ = 1 << 0, - CLC_KERNEL_ARG_ACCESS_WRITE = 1 << 1, -}; - -enum clc_kernel_arg_address_qualifier { - CLC_KERNEL_ARG_ADDRESS_PRIVATE, - CLC_KERNEL_ARG_ADDRESS_CONSTANT, - CLC_KERNEL_ARG_ADDRESS_LOCAL, - CLC_KERNEL_ARG_ADDRESS_GLOBAL, -}; - -struct clc_kernel_arg { - const char *name; - const char *type_name; - unsigned type_qualifier; - unsigned access_qualifier; - enum clc_kernel_arg_address_qualifier address_qualifier; -}; - -enum clc_vec_hint_type { - CLC_VEC_HINT_TYPE_CHAR = 0, - CLC_VEC_HINT_TYPE_SHORT = 1, - CLC_VEC_HINT_TYPE_INT = 2, - CLC_VEC_HINT_TYPE_LONG = 3, - CLC_VEC_HINT_TYPE_HALF = 4, - CLC_VEC_HINT_TYPE_FLOAT = 5, - CLC_VEC_HINT_TYPE_DOUBLE = 6 -}; - -struct clc_kernel_info { - const char *name; - size_t num_args; - const struct clc_kernel_arg *args; - - unsigned vec_hint_size; - enum clc_vec_hint_type vec_hint_type; -}; - -enum clc_spec_constant_type { - CLC_SPEC_CONSTANT_UNKNOWN, - CLC_SPEC_CONSTANT_BOOL, - CLC_SPEC_CONSTANT_FLOAT, - CLC_SPEC_CONSTANT_DOUBLE, - CLC_SPEC_CONSTANT_INT8, - CLC_SPEC_CONSTANT_UINT8, - CLC_SPEC_CONSTANT_INT16, - CLC_SPEC_CONSTANT_UINT16, - CLC_SPEC_CONSTANT_INT32, - CLC_SPEC_CONSTANT_UINT32, - CLC_SPEC_CONSTANT_INT64, - CLC_SPEC_CONSTANT_UINT64, -}; - -struct clc_parsed_spec_constant { - uint32_t id; - enum clc_spec_constant_type type; -}; - -struct clc_parsed_spirv { - const struct clc_kernel_info *kernels; - unsigned num_kernels; - - const struct clc_parsed_spec_constant *spec_constants; - unsigned num_spec_constants; -}; - #define CLC_MAX_CONSTS 32 #define CLC_MAX_BINDINGS_PER_ARG 3 #define CLC_MAX_SAMPLERS 16 @@ -209,54 +103,6 @@ struct clc_dxil_object { } binary; }; -struct clc_libclc; - -struct clc_libclc_options { - unsigned optimize; -}; - -struct clc_libclc *clc_libclc_new(const struct clc_logger *logger, const struct clc_libclc_options *options); - -void clc_free_libclc(struct clc_libclc *lib); - -void clc_libclc_serialize(struct clc_libclc *lib, void **serialized, size_t *size); -void clc_libclc_free_serialized(void *serialized); -struct clc_libclc *clc_libclc_deserialize(void *serialized, size_t size); - -bool -clc_compile_c_to_spir(const struct clc_compile_args *args, - const struct clc_logger *logger, - struct clc_binary *out_spir); - -void -clc_free_spir(struct clc_binary *spir); - -bool -clc_compile_spir_to_spirv(const struct clc_binary *in_spir, - const struct clc_logger *logger, - struct clc_binary *out_spirv); - -void -clc_free_spirv(struct clc_binary *spirv); - -bool -clc_compile_c_to_spirv(const struct clc_compile_args *args, - const struct clc_logger *logger, - struct clc_binary *out_spirv); - -bool -clc_link_spirv(const struct clc_linker_args *args, - const struct clc_logger *logger, - struct clc_binary *out_spirv); - -bool -clc_parse_spirv(const struct clc_binary *in_spirv, - const struct clc_logger *logger, - struct clc_parsed_spirv *out_data); - -void -clc_free_parsed_spirv(struct clc_parsed_spirv *data); - struct clc_runtime_arg_info { union { struct { @@ -278,36 +124,13 @@ struct clc_runtime_kernel_conf { unsigned support_workgroup_id_offsets; }; -typedef union { - bool b; - float f32; - double f64; - int8_t i8; - uint8_t u8; - int16_t i16; - uint16_t u16; - int32_t i32; - uint32_t u32; - int64_t i64; - uint64_t u64; -} clc_spirv_const_value; - -struct clc_spirv_specialization { - uint32_t id; - clc_spirv_const_value value; - bool defined_on_module; +struct clc_libclc_dxil_options { + unsigned optimize; }; -struct clc_spirv_specialization_consts { - const struct clc_spirv_specialization *specializations; - unsigned num_specializations; -}; - -bool -clc_specialize_spirv(const struct clc_binary *in_spirv, - const struct clc_parsed_spirv *parsed_data, - const struct clc_spirv_specialization_consts *consts, - struct clc_binary *out_spirv); +struct clc_libclc * +clc_libclc_new_dxil(const struct clc_logger *logger, + const struct clc_libclc_dxil_options *dxil_options); bool clc_spirv_to_dxil(struct clc_libclc *lib, diff --git a/src/microsoft/clc/clon12compiler.def b/src/microsoft/clc/clon12compiler.def index bb93d4b8c9e7b..a23632c6c96f6 100644 --- a/src/microsoft/clc/clon12compiler.def +++ b/src/microsoft/clc/clon12compiler.def @@ -1,5 +1,5 @@ EXPORTS - clc_libclc_new + clc_libclc_new_dxil clc_free_libclc clc_libclc_serialize clc_libclc_free_serialized diff --git a/src/microsoft/clc/compute_test.cpp b/src/microsoft/clc/compute_test.cpp index e5faa4f5dde8e..1a51a32bfd67e 100644 --- a/src/microsoft/clc/compute_test.cpp +++ b/src/microsoft/clc/compute_test.cpp @@ -622,10 +622,10 @@ ComputeTest::SetUp() static struct clc_libclc *compiler_ctx_g = nullptr; if (!compiler_ctx_g) { - clc_libclc_options options = { }; + clc_libclc_dxil_options options = { }; options.optimize = (debug_get_option_debug_compute() & COMPUTE_DEBUG_OPTIMIZE_LIBCLC) != 0; - compiler_ctx_g = clc_libclc_new(&logger, &options); + compiler_ctx_g = clc_libclc_new_dxil(&logger, &options); if (!compiler_ctx_g) throw runtime_error("failed to create CLC compiler context"); diff --git a/src/microsoft/clc/meson.build b/src/microsoft/clc/meson.build index fb53f5445bdc3..e30bf33d5b758 100644 --- a/src/microsoft/clc/meson.build +++ b/src/microsoft/clc/meson.build @@ -19,52 +19,27 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. -clang_ver_dir = dep_clang.version() -if clang_ver_dir.endswith('git') - clang_ver_dir = clang_ver_dir.substring(0, -3) -endif - -clang_resource_dir = join_paths( - dep_clang.get_variable(cmake: 'CLANG_INCLUDE_DIRS'), '..', - 'lib', 'clang', clang_ver_dir, 'include' -) - -opencl_c_h = custom_target( - 'opencl-c.h', - input : [files_xxd, join_paths(clang_resource_dir, 'opencl-c.h')], - output : 'opencl-c.h.h', - command : [prog_python, '@INPUT@', '@OUTPUT@', '-n', 'opencl_c_source'], -) -opencl_c_base_h = custom_target( - 'opencl-c-base.h', - input : [files_xxd, join_paths(clang_resource_dir, 'opencl-c-base.h')], - output : 'opencl-c-base.h.h', - command : [prog_python, '@INPUT@', '@OUTPUT@', '-n', 'opencl_c_base_source'], -) - files_libclc_compiler = files( 'clc_compiler.c', 'clc_nir.c', - 'clc_helpers.cpp' ) libclc_compiler = shared_library( 'clon12compiler', [files_libclc_compiler, sha1_h], - opencl_c_h, - opencl_c_base_h, vs_module_defs : 'clon12compiler.def', include_directories : [inc_include, inc_src, inc_mapi, inc_mesa, inc_compiler, inc_gallium, inc_spirv], - dependencies: [idep_nir_headers, dep_clang, dep_llvm, dep_version, - dep_llvmspirvlib, idep_mesautil, idep_libdxil_compiler, idep_nir, dep_spirv_tools] + dependencies: [idep_clc, idep_nir_headers, dep_version, idep_mesautil, + idep_libdxil_compiler, idep_nir] ) if dep_dxheaders.found() clc_compiler_test = executable('clc_compiler_test', ['clc_compiler_test.cpp', 'compute_test.cpp'], link_with : [libclc_compiler], - dependencies : [idep_gtest, idep_mesautil, idep_libdxil_compiler, dep_dxheaders, dep_spirv_tools], - include_directories : [inc_include, inc_src, inc_spirv]) + dependencies : [idep_gtest, idep_mesautil, idep_libdxil_compiler, dep_dxheaders, + dep_spirv_tools], + include_directories : [inc_include, inc_src, inc_compiler, inc_spirv]) test('clc_compiler_test', clc_compiler_test, timeout: 180) endif