From a8dbbaf25a3b4aea96178302cb71aceb18db88ec Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 27 Jul 2017 04:35:05 -0700 Subject: [PATCH] compiling test shader to LLVM IR works --- .gitignore | 2 +- src/demo/demo.cpp | 49 +++--- src/llvm_wrapper/CMakeLists.txt | 6 +- src/llvm_wrapper/llvm_wrapper.h | 8 + src/spirv_to_llvm/spirv_to_llvm.cpp | 264 +++++++++++++++++++++++----- test-files/test.spv | Bin 0 -> 916 bytes 6 files changed, 260 insertions(+), 69 deletions(-) create mode 100644 test-files/test.spv diff --git a/.gitignore b/.gitignore index 14edeb5..bc08171 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ cmake_install.cmake CMakeCache.txt Makefile /.settings/ -/test-files/test.spv +/test-files/test2.spv out.json compile_commands.json /.kdev4/ diff --git a/src/demo/demo.cpp b/src/demo/demo.cpp index b8c89ca..13bcec5 100644 --- a/src/demo/demo.cpp +++ b/src/demo/demo.cpp @@ -126,19 +126,19 @@ util::optional> load_file(const char *filename) void dump_words(const spirv::Word *words, std::size_t word_count) { constexpr std::size_t max_words_per_line = 4; - auto old_fill = std::cout.fill('0'); - auto old_flags = std::cout.flags(std::ios::uppercase | std::ios::hex | std::ios::right); - auto old_width = std::cout.width(); + auto old_fill = std::cerr.fill('0'); + auto old_flags = std::cerr.flags(std::ios::uppercase | std::ios::hex | std::ios::right); + auto old_width = std::cerr.width(); bool wrote_line_beginning = false; bool wrote_line_ending = true; - std::cout << "Words:\n"; + std::cerr << "Words:\n"; std::size_t current_words_per_line = 0; std::size_t index; auto seperator = ""; auto write_line_beginning = [&]() { - std::cout.width(8); - std::cout << index << ":"; + std::cerr.width(8); + std::cerr << index << ":"; seperator = " "; wrote_line_beginning = true; wrote_line_ending = false; @@ -148,15 +148,15 @@ void dump_words(const spirv::Word *words, std::size_t word_count) { while(current_words_per_line < max_words_per_line) { - std::cout << seperator; + std::cerr << seperator; seperator = " "; - std::cout.width(8); - std::cout.fill(' '); - std::cout << ""; - std::cout.fill('0'); + std::cerr.width(8); + std::cerr.fill(' '); + std::cerr << ""; + std::cerr.fill('0'); current_words_per_line++; } - std::cout << seperator << " |" << chars << "|\n"; + std::cerr << seperator << " |" << chars << "|\n"; seperator = ""; wrote_line_ending = true; wrote_line_beginning = false; @@ -172,10 +172,10 @@ void dump_words(const spirv::Word *words, std::size_t word_count) }; auto write_word = [&](spirv::Word w) { - std::cout << seperator; + std::cerr << seperator; seperator = " "; - std::cout.width(8); - std::cout << w; + std::cerr.width(8); + std::cerr << w; current_words_per_line++; append_char(w & 0xFFU); append_char((w >> 8) & 0xFFU); @@ -192,10 +192,10 @@ void dump_words(const spirv::Word *words, std::size_t word_count) } if(!wrote_line_ending) write_line_ending(); - std::cout.flush(); - std::cout.width(old_width); - std::cout.fill(old_fill); - std::cout.flags(old_flags); + std::cerr.flush(); + std::cerr.width(old_width); + std::cerr.fill(old_fill); + std::cerr.flags(old_flags); } void dump_words(const std::vector &words) @@ -215,12 +215,13 @@ int test_main(int argc, char **argv) } filename = argv[1]; } - std::cout << "loading " << filename << std::endl; + std::cerr << "loading " << filename << std::endl; auto file = load_file(filename); if(file) { { dump_words(*file); + std::cerr << std::endl; spirv::Dump_callbacks dump_callbacks; try { @@ -228,11 +229,11 @@ int test_main(int argc, char **argv) } catch(spirv::Parser_error &e) { - std::cout << dump_callbacks.ss.str() << std::endl; + std::cerr << dump_callbacks.ss.str() << std::endl; std::cerr << "error: " << e.what(); return 1; } - std::cout << dump_callbacks.ss.str() << std::endl; + std::cerr << dump_callbacks.ss.str() << std::endl; } auto llvm_context = llvm_wrapper::Context::create(); std::uint64_t next_module_id = 1; @@ -246,7 +247,11 @@ int test_main(int argc, char **argv) std::cerr << "error: " << e.what(); return 1; } + std::cerr << "Translation to LLVM succeeded." << std::endl; ::LLVMDumpModule(converted_module.module.get()); + bool failed = ::LLVMVerifyModule(converted_module.module.get(), ::LLVMPrintMessageAction, nullptr); + if(failed) + return 1; } else { diff --git a/src/llvm_wrapper/CMakeLists.txt b/src/llvm_wrapper/CMakeLists.txt index 0509ad7..4934770 100644 --- a/src/llvm_wrapper/CMakeLists.txt +++ b/src/llvm_wrapper/CMakeLists.txt @@ -22,5 +22,9 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) set(sources llvm_wrapper.cpp) add_library(llvm_wrapper STATIC ${sources}) -llvm_map_components_to_libnames(llvm_libraries core mcjit native) +if(0) +llvm_map_components_to_libnames(llvm_libraries core mcjit native analysis) +else() +set(llvm_libraries LLVM) +endif() target_link_libraries(llvm_wrapper util ${llvm_libraries}) diff --git a/src/llvm_wrapper/llvm_wrapper.h b/src/llvm_wrapper/llvm_wrapper.h index 565c335..7457c7d 100644 --- a/src/llvm_wrapper/llvm_wrapper.h +++ b/src/llvm_wrapper/llvm_wrapper.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -311,6 +312,13 @@ struct Builder : public Wrapper<::LLVMBuilderRef, Builder_deleter> return Builder(::LLVMCreateBuilderInContext(context)); } }; + +inline ::LLVMTypeRef get_scalar_or_vector_element_type(::LLVMTypeRef type) +{ + if(::LLVMGetTypeKind(type) == ::LLVMTypeKind::LLVMVectorTypeKind) + return ::LLVMGetElementType(type); + return type; +} } } diff --git a/src/spirv_to_llvm/spirv_to_llvm.cpp b/src/spirv_to_llvm/spirv_to_llvm.cpp index d2531d8..c1257f9 100644 --- a/src/spirv_to_llvm/spirv_to_llvm.cpp +++ b/src/spirv_to_llvm/spirv_to_llvm.cpp @@ -26,6 +26,7 @@ #include "util/enum.h" #include #include +#include namespace vulkan_cpu { @@ -44,7 +45,7 @@ constexpr std::size_t get_padding_size(std::size_t current_position, std::size_t needed_alignment) noexcept { assert(is_power_of_2(needed_alignment)); - return ~current_position & (needed_alignment - 1); + return -current_position & (needed_alignment - 1); } } @@ -613,7 +614,7 @@ private: util::Enum_set enabled_capabilities; ::LLVMContextRef context; [[gnu::unused]] const std::uint64_t shader_id; - std::string name_prefix; + std::string name_prefix_string; llvm_wrapper::Module module; std::shared_ptr io_struct; static constexpr std::size_t io_struct_argument_index = 0; @@ -661,10 +662,16 @@ private: { auto &function = get_id_state(current_function_id).function.value(); state.label = Label_state(::LLVMAppendBasicBlockInContext( - context, function.function, (name_prefix + get_name(id)).c_str())); + context, function.function, get_prefixed_name(get_name(id)).c_str())); } return state.label->basic_block; } + std::string get_prefixed_name(std::string name) const + { + if(!name.empty()) + return name_prefix_string + std::move(name); + return name; + } public: explicit Spirv_to_llvm(::LLVMContextRef context, std::uint64_t shader_id) @@ -673,9 +680,9 @@ public: { std::ostringstream ss; ss << "shader_" << shader_id << "_"; - name_prefix = ss.str(); + name_prefix_string = ss.str(); } - module = llvm_wrapper::Module::create_native((name_prefix + "module").c_str(), context); + module = llvm_wrapper::Module::create_native(get_prefixed_name("module").c_str(), context); builder = llvm_wrapper::Builder::create(context); auto target_data = ::LLVMGetModuleDataLayout(module.get()); constexpr std::size_t no_instruction_index = 0; @@ -683,7 +690,7 @@ public: std::make_shared(std::vector{}, context, target_data, - (name_prefix + "Io_struct").c_str(), + get_prefixed_name("Io_struct").c_str(), no_instruction_index); assert(implicit_function_arguments.size() == 1); static_assert(io_struct_argument_index == 0, ""); @@ -694,14 +701,14 @@ public: std::make_shared(std::vector{}, context, target_data, - (name_prefix + "Inputs").c_str(), + get_prefixed_name("Inputs").c_str(), no_instruction_index); inputs_member = io_struct->add_member(Struct_type_descriptor::Member({}, inputs_struct)); outputs_struct = std::make_shared(std::vector{}, context, target_data, - (name_prefix + "Outputs").c_str(), + get_prefixed_name("Outputs").c_str(), no_instruction_index); outputs_member = io_struct->add_member(Struct_type_descriptor::Member({}, outputs_struct)); } @@ -2292,7 +2299,7 @@ void Spirv_to_llvm::handle_instruction_op_type_struct(Op_type_struct instruction state.decorations, context, ::LLVMGetModuleDataLayout(module.get()), - (name_prefix + get_name(instruction.result)).c_str(), + get_prefixed_name(get_name(instruction.result)).c_str(), instruction_start_index, std::move(members)); break; @@ -2463,6 +2470,11 @@ void Spirv_to_llvm::handle_instruction_op_constant(Op_constant instruction, case Stage::calculate_types: { auto &state = get_id_state(instruction.result); + if(!state.decorations.empty()) + throw Parser_error(instruction_start_index, + instruction_start_index, + "decorations on instruction not implemented: " + + std::string(get_enumerant_name(instruction.get_operation()))); auto type = get_type(instruction.result_type, instruction_start_index); if(auto *simple_type = dynamic_cast(type.get())) { @@ -2687,13 +2699,18 @@ void Spirv_to_llvm::handle_instruction_op_function(Op_function instruction, case Stage::generate_code: { auto &state = get_id_state(current_function_id); + if(!state.decorations.empty()) + throw Parser_error(instruction_start_index, + instruction_start_index, + "decorations on instruction not implemented: " + + std::string(get_enumerant_name(instruction.get_operation()))); auto function_type = get_type(instruction.function_type, instruction_start_index); - state.function = - Function_state(function_type, - ::LLVMAddFunction(module.get(), - (name_prefix + get_name(current_function_id)).c_str(), - function_type->get_or_make_type())); + state.function = Function_state( + function_type, + ::LLVMAddFunction(module.get(), + get_prefixed_name(get_name(current_function_id)).c_str(), + function_type->get_or_make_type())); break; } } @@ -2758,7 +2775,9 @@ void Spirv_to_llvm::handle_instruction_op_variable(Op_variable instruction, throw Parser_error(instruction_start_index, instruction_start_index, "shader input variable initializers are not implemented"); - auto type = get_type(instruction.result_type, instruction_start_index); + auto type = get_type(instruction.result_type, + instruction_start_index) + ->get_base_type(); state.variable = Input_variable_state{type, inputs_struct->add_member(Struct_type_descriptor::Member( @@ -2775,7 +2794,9 @@ void Spirv_to_llvm::handle_instruction_op_variable(Op_variable instruction, throw Parser_error(instruction_start_index, instruction_start_index, "shader output variable initializers are not implemented"); - auto type = get_type(instruction.result_type, instruction_start_index); + auto type = get_type(instruction.result_type, + instruction_start_index) + ->get_base_type(); state.variable = Output_variable_state{type, outputs_struct->add_member(Struct_type_descriptor::Member( @@ -3064,11 +3085,12 @@ void Spirv_to_llvm::handle_instruction_op_variable(Op_variable instruction, throw Parser_error(instruction_start_index, instruction_start_index, "function-local variable must be inside initial basic block"); - auto type = get_type(instruction.result_type, instruction_start_index); - state.value = Value( - ::LLVMBuildAlloca( - builder.get(), type->get_or_make_type(), get_name(instruction.result).c_str()), - type); + auto type = + get_type(instruction.result_type, instruction_start_index); + state.value = Value(::LLVMBuildAlloca(builder.get(), + type->get_base_type()->get_or_make_type(), + get_name(instruction.result).c_str()), + type); return; } case Storage_class::generic: @@ -3112,6 +3134,11 @@ void Spirv_to_llvm::handle_instruction_op_load(Op_load instruction, case Stage::generate_code: { auto &state = get_id_state(instruction.result); + if(!state.decorations.empty()) + throw Parser_error(instruction_start_index, + instruction_start_index, + "decorations on instruction not implemented: " + + std::string(get_enumerant_name(instruction.get_operation()))); auto memory_access = instruction.memory_access.value_or( Memory_access_with_parameters(Memory_access::none, {})); if((memory_access.value & Memory_access::volatile_) == Memory_access::volatile_) @@ -3144,11 +3171,23 @@ void Spirv_to_llvm::handle_instruction_op_store(Op_store instruction, break; case Stage::generate_code: { -#warning finish - throw Parser_error(instruction_start_index, - instruction_start_index, - "instruction not implemented: " - + std::string(get_enumerant_name(instruction.get_operation()))); + auto memory_access = instruction.memory_access.value_or( + Memory_access_with_parameters(Memory_access::none, {})); + if((memory_access.value & Memory_access::volatile_) == Memory_access::volatile_) + throw Parser_error(instruction_start_index, + instruction_start_index, + "OpStore volatile not implemented"); + if((memory_access.value & Memory_access::aligned) == Memory_access::aligned) + throw Parser_error(instruction_start_index, + instruction_start_index, + "OpStore alignment not implemented"); + if((memory_access.value & Memory_access::nontemporal) == Memory_access::nontemporal) + throw Parser_error(instruction_start_index, + instruction_start_index, + "OpStore nontemporal not implemented"); + ::LLVMBuildStore(builder.get(), + get_id_state(instruction.object).value.value().value, + get_id_state(instruction.pointer).value.value().value); break; } } @@ -3183,11 +3222,86 @@ void Spirv_to_llvm::handle_instruction_op_access_chain(Op_access_chain instructi break; case Stage::generate_code: { + auto &state = get_id_state(instruction.result); + if(!state.decorations.empty()) + throw Parser_error(instruction_start_index, + instruction_start_index, + "decorations on instruction not implemented: " + + std::string(get_enumerant_name(instruction.get_operation()))); + auto base = get_id_state(instruction.base).value.value(); + std::string name = get_name(instruction.result); + std::vector<::LLVMValueRef> llvm_indexes; + llvm_indexes.reserve(instruction.indexes.size() + 1); + auto *base_pointer_type = dynamic_cast(base.type.get()); + if(!base_pointer_type) + throw Parser_error(instruction_start_index, + instruction_start_index, + "base type is not a pointer for OpAccessChain"); + llvm_indexes.push_back(::LLVMConstInt(::LLVMInt32TypeInContext(context), 0, false)); + auto current_type = base_pointer_type->get_base_type(); + for(std::size_t i = 0; i < instruction.indexes.size(); i++) + { + Id index = instruction.indexes[i]; + struct Visitor + { + std::size_t instruction_start_index; + std::shared_ptr ¤t_type; + std::vector<::LLVMValueRef> &llvm_indexes; + Id index; + Spirv_to_llvm *this_; + void operator()(Simple_type_descriptor &) + { + throw Parser_error(instruction_start_index, + instruction_start_index, + "invalid composite type for OpAccessChain"); + } + void operator()(Vector_type_descriptor &type) + { + auto &index_value = this_->get_id_state(index).value.value(); + llvm_indexes.push_back(index_value.value); + current_type = type.get_element_type(); + } + void operator()(Matrix_type_descriptor &) + { #warning finish - throw Parser_error(instruction_start_index, - instruction_start_index, - "instruction not implemented: " - + std::string(get_enumerant_name(instruction.get_operation()))); + throw Parser_error(instruction_start_index, + instruction_start_index, + "unimplemented composite type for OpAccessChain"); + } + void operator()(Pointer_type_descriptor &) + { + throw Parser_error(instruction_start_index, + instruction_start_index, + "invalid composite type for OpAccessChain"); + } + void operator()(Function_type_descriptor &) + { + throw Parser_error(instruction_start_index, + instruction_start_index, + "invalid composite type for OpAccessChain"); + } + void operator()(Struct_type_descriptor &type) + { + auto index_value = ::LLVMConstIntGetZExtValue( + this_->get_id_state(index).constant->get_or_make_value()); + auto &members = type.get_members(true); + if(index_value >= members.size()) + throw Parser_error(instruction_start_index, + instruction_start_index, + "index out of range in OpAccessChain"); + llvm_indexes.push_back(::LLVMConstInt(::LLVMInt32TypeInContext(this_->context), + members[index_value].llvm_member_index, + false)); + current_type = members[index_value].type; + } + }; + auto *type = current_type.get(); + type->visit(Visitor{instruction_start_index, current_type, llvm_indexes, index, this}); + } + state.value = Value( + ::LLVMBuildGEP( + builder.get(), base.value, llvm_indexes.data(), llvm_indexes.size(), name.c_str()), + get_type(instruction.result_type, instruction_start_index)); break; } } @@ -3326,6 +3440,11 @@ void Spirv_to_llvm::handle_instruction_op_composite_construct(Op_composite_const case Stage::generate_code: { auto &state = get_id_state(instruction.result); + if(!state.decorations.empty()) + throw Parser_error(instruction_start_index, + instruction_start_index, + "decorations on instruction not implemented: " + + std::string(get_enumerant_name(instruction.get_operation()))); auto result_type = get_type(instruction.result_type, instruction_start_index); ::LLVMValueRef result_value = nullptr; std::string name = get_name(instruction.result); @@ -3437,6 +3556,11 @@ void Spirv_to_llvm::handle_instruction_op_composite_extract(Op_composite_extract case Stage::generate_code: { auto &state = get_id_state(instruction.result); + if(!state.decorations.empty()) + throw Parser_error(instruction_start_index, + instruction_start_index, + "decorations on instruction not implemented: " + + std::string(get_enumerant_name(instruction.get_operation()))); auto result = get_id_state(instruction.composite).value.value(); std::string name = ""; for(std::size_t i = 0; i < instruction.indexes.size(); i++) @@ -3769,11 +3893,19 @@ void Spirv_to_llvm::handle_instruction_op_convert_f_to_u(Op_convert_f_to_u instr break; case Stage::generate_code: { -#warning finish - throw Parser_error(instruction_start_index, - instruction_start_index, - "instruction not implemented: " - + std::string(get_enumerant_name(instruction.get_operation()))); + auto &state = get_id_state(instruction.result); + if(!state.decorations.empty()) + throw Parser_error(instruction_start_index, + instruction_start_index, + "decorations on instruction not implemented: " + + std::string(get_enumerant_name(instruction.get_operation()))); + auto result_type = get_type(instruction.result_type, instruction_start_index); + state.value = + Value(::LLVMBuildFPToUI(builder.get(), + get_id_state(instruction.float_value).value.value().value, + result_type->get_or_make_type(), + get_name(instruction.result).c_str()), + result_type); break; } } @@ -3818,11 +3950,27 @@ void Spirv_to_llvm::handle_instruction_op_u_convert(Op_u_convert instruction, break; case Stage::generate_code: { -#warning finish - throw Parser_error(instruction_start_index, - instruction_start_index, - "instruction not implemented: " - + std::string(get_enumerant_name(instruction.get_operation()))); + auto &state = get_id_state(instruction.result); + if(!state.decorations.empty()) + throw Parser_error(instruction_start_index, + instruction_start_index, + "decorations on instruction not implemented: " + + std::string(get_enumerant_name(instruction.get_operation()))); + auto result_type = get_type(instruction.result_type, instruction_start_index); + auto result_type_int_width = ::LLVMGetIntTypeWidth( + llvm_wrapper::get_scalar_or_vector_element_type(result_type->get_or_make_type())); + auto &arg = get_id_state(instruction.unsigned_value).value.value(); + auto arg_int_width = ::LLVMGetIntTypeWidth( + llvm_wrapper::get_scalar_or_vector_element_type(arg.type->get_or_make_type())); + auto opcode = ::LLVMTrunc; + if(result_type_int_width > arg_int_width) + opcode = ::LLVMZExt; + state.value = Value(::LLVMBuildCast(builder.get(), + opcode, + arg.value, + result_type->get_or_make_type(), + get_name(instruction.result).c_str()), + result_type); break; } } @@ -3937,11 +4085,33 @@ void Spirv_to_llvm::handle_instruction_op_bitcast(Op_bitcast instruction, break; case Stage::generate_code: { -#warning finish - throw Parser_error(instruction_start_index, - instruction_start_index, - "instruction not implemented: " - + std::string(get_enumerant_name(instruction.get_operation()))); + auto &state = get_id_state(instruction.result); + if(!state.decorations.empty()) + throw Parser_error(instruction_start_index, + instruction_start_index, + "decorations on instruction not implemented: " + + std::string(get_enumerant_name(instruction.get_operation()))); + auto result_type = get_type(instruction.result_type, instruction_start_index); + auto &arg = get_id_state(instruction.operand).value.value(); + std::size_t result_element_count = 1; // scalar is equivalent to size 1 vector + std::size_t arg_element_count = 1; + if(auto *t = dynamic_cast(result_type.get())) + result_element_count = t->get_element_count(); + if(auto *t = dynamic_cast(result_type.get())) + arg_element_count = t->get_element_count(); + if(result_element_count != arg_element_count) + { +// need to bitcast as if on little endian system even on big endian +#warning finish implementing element-count-changing bitcast + throw Parser_error(instruction_start_index, + instruction_start_index, + "element-count-changing OpBitcast is not implemented"); + } + state.value = Value(::LLVMBuildBitCast(builder.get(), + arg.value, + result_type->get_or_make_type(), + get_name(instruction.result).c_str()), + result_type); break; } } @@ -5070,6 +5240,10 @@ void Spirv_to_llvm::handle_instruction_op_label(Op_label instruction, case Stage::generate_code: { auto &function = get_id_state(current_function_id).function.value(); + if(!get_id_state(current_basic_block_id).decorations.empty()) + throw Parser_error(instruction_start_index, + instruction_start_index, + "decorations on label not implemented"); auto block = get_or_make_label(instruction.result); ::LLVMPositionBuilderAtEnd(builder.get(), block); if(!function.entry_block) diff --git a/test-files/test.spv b/test-files/test.spv new file mode 100644 index 0000000000000000000000000000000000000000..b48dc28472fd106af32d97899cb5844cf016e453 GIT binary patch literal 916 zcmYk4OD{uV5Qe{AT}rE}t!tesf=Xk7NW`V9T697D0*Q@9CBo9ezeYsjc}~y4G;e0+ z{pNm}%XTJ0$ncvC`(Z5Aki`!{T|$>aG4=<;+hOnd<-T`#bYNsTl8a!b(B@hH zH1VB9u8!{v5pc(D;?H0!)}BRM+j#4h(Z3><MEI`7^E=}vwV`^x#fK8dxDIp+TN<-h580)7CbZzDwj literal 0 HcmV?d00001 -- 2.30.2