diff options
author | Luca Barbieri <luca@luca-barbieri.com> | 2010-08-05 12:20:33 +0200 |
---|---|---|
committer | Luca Barbieri <luca@luca-barbieri.com> | 2010-08-19 20:49:13 +0200 |
commit | 59726683bc9ac41276e8d5498b9241cdda0ecb92 (patch) | |
tree | d2b2a9447d39279619d5ae622e22326d35271c02 | |
parent | 076c53879b90855ecf38602584f22e4ab6db7569 (diff) |
GLSL2->LLVM conversion code and "glslang" compilerglsl2-llvm
It should support everything except texture and derivative operations.
It seems to work.
This is right now only a proof of concept, and there is no graphics
pipeline you can use the result in.
Comments, enhancements, etc. welcome.
This can be merged into master if the GLSL2 maintainers want that.
-rw-r--r-- | src/glsl/Makefile | 17 | ||||
-rw-r--r-- | src/glsl/glslang.cpp | 364 | ||||
-rw-r--r-- | src/glsl/ir_to_llvm.cpp | 1117 | ||||
-rw-r--r-- | src/glsl/ir_to_llvm.h | 9 |
4 files changed, 1507 insertions, 0 deletions
diff --git a/src/glsl/Makefile b/src/glsl/Makefile index 1d200b47b4..e1d01004e7 100644 --- a/src/glsl/Makefile +++ b/src/glsl/Makefile @@ -89,6 +89,14 @@ GLSL2_OBJECTS = \ $(GLSL2_C_SOURCES:.c=.o) \ $(GLSL2_CXX_SOURCES:.cpp=.o) +GLSLANG_CXX_SOURCES = \ + glslang.cpp \ + ir_to_llvm.cpp + +GLSLANG_OBJECTS = \ + $(GLSL2_C_SOURCES:.c=.o) \ + $(GLSLANG_CXX_SOURCES:.cpp=.o) + ### Basic defines ### DEFINES += \ @@ -140,12 +148,21 @@ install: ##### RULES ##### +glslang: $(GLSLANG_OBJECTS) libglsl.a + $(APP_CXX) $(INCLUDES) $(CFLAGS) $(LDFLAGS) $(GLSLANG_OBJECTS) $(LIBS) $(LLVM_LIBS) -lpthread -ldl -o $@ + glsl_compiler: $(GLSL2_OBJECTS) libglsl.a $(APP_CXX) $(INCLUDES) $(CFLAGS) $(LDFLAGS) $(GLSL2_OBJECTS) $(LIBS) -o $@ glcpp/glcpp: $(GLCPP_OBJECTS) libglsl.a $(APP_CC) $(INCLUDES) $(CFLAGS) $(LDFLAGS) $(GLCPP_OBJECTS) $(LIBS) -o $@ +ir_to_llvm.o: ir_to_llvm.cpp + $(CXX) -c $(INCLUDES) $(LLVM_CFLAGS) $(CXXFLAGS) $(DEFINES) $< -o $@ + +glslang.o: glslang.cpp + $(CXX) -c $(INCLUDES) $(LLVM_CFLAGS) $(CXXFLAGS) $(DEFINES) $< -o $@ + .cpp.o: $(CXX) -c $(INCLUDES) $(CXXFLAGS) $(DEFINES) $< -o $@ diff --git a/src/glsl/glslang.cpp b/src/glsl/glslang.cpp new file mode 100644 index 0000000000..77c61c3644 --- /dev/null +++ b/src/glsl/glslang.cpp @@ -0,0 +1,364 @@ +/* + * Copyright (C) 2005-2007 Brian Paul All Rights Reserved. + * Copyright (C) 2008 VMware, Inc. All Rights Reserved. + * Copyright © 2010 Intel Corporation + * Copyright © 2010 Luca Barbieri + * + * 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 "ir_to_llvm.h" + +#include <llvm/Bitcode/ReaderWriter.h> +#include <llvm/Support/raw_ostream.h> + +#include <vector> +#include <iostream> +#include <fstream> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <getopt.h> +#include <stdio.h> +#ifdef _MSC_VER +#include <unordered_map> +#else +#include <tr1/unordered_map> +#endif +// use C++0x/Microsoft convention +namespace std +{ + using namespace tr1; +} + +#include "ir.h" +#include "ir_visitor.h" +#include "ir_print_visitor.h" +#include "ir_expression_flattening.h" +#include "glsl_types.h" +#include "glsl_parser_extras.h" +#include "../glsl/program.h" +#include "ir_optimization.h" +#include "ast.h" + +extern "C" struct gl_shader * +_mesa_new_shader(GLcontext *ctx, GLuint name, GLenum type); + +/* Copied from shader_api.c for the stand-alone compiler. + */ +struct gl_shader * +_mesa_new_shader(GLcontext *ctx, GLuint name, GLenum type) +{ + struct gl_shader *shader; + + (void) ctx; + + assert(type == GL_FRAGMENT_SHADER || type == GL_VERTEX_SHADER); + shader = talloc_zero(NULL, struct gl_shader); + if (shader) { + shader->Type = type; + shader->Name = name; + shader->RefCount = 1; + } + return shader; +} + +/* Returned string will have 'ctx' as its talloc owner. */ +static char * +load_text_file(void *ctx, const char *file_name) +{ + char *text = NULL; + struct stat st; + ssize_t total_read = 0; + int fd = open(file_name, O_RDONLY); + + if (fd < 0) + return NULL; + + if (fstat(fd, &st) == 0) + { + text = (char *) talloc_size(ctx, st.st_size + 1); + if (text != NULL) + { + do + { + ssize_t bytes = read(fd, text + total_read, st.st_size - total_read); + if (bytes < 0) + { + free(text); + text = NULL; + break; + } + + if (bytes == 0) + { + break; + } + + total_read += bytes; + } while (total_read < st.st_size); + + text[total_read] = '\0'; + } + } + + close(fd); + + return text; +} + +void +usage_fail(const char *name) +{ + printf("%s <filename.frag|filename.vert>\n", name); + exit(EXIT_FAILURE); +} + + +int dump_ast = 0; +int dump_hir = 0; +int dump_lir = 0; +int dump_llvm = 0; +int do_link = 0; + +const struct option compiler_opts[] = { + { "dump-ast", 0, &dump_ast, 1 }, + { "dump-hir", 0, &dump_hir, 1 }, + { "dump-lir", 0, &dump_lir, 1 }, + { "dump-llvm", 0, &dump_llvm, 1 }, + { "link", 0, &do_link, 1 }, + { NULL, 0, NULL, 0 } +}; + +void +compile_shader(struct gl_shader *shader) +{ + struct _mesa_glsl_parse_state *state = + new(shader) _mesa_glsl_parse_state(NULL, shader->Type, shader); + + const char *source = shader->Source; + state->error = preprocess(state, &source, &state->info_log, + state->extensions); + + if (!state->error) { + _mesa_glsl_lexer_ctor(state, source); + _mesa_glsl_parse(state); + _mesa_glsl_lexer_dtor(state); + } + + if (dump_ast) { + foreach_list_const(n, &state->translation_unit) { + ast_node *ast = exec_node_data(ast_node, n, link); + ast->print(); + } + printf("\n\n"); + } + + shader->ir = new(shader) exec_list; + if (!state->error && !state->translation_unit.is_empty()) + _mesa_ast_to_hir(shader->ir, state); + + /* Print out the unoptimized IR. */ + if (!state->error && dump_hir) { + validate_ir_tree(shader->ir); + _mesa_print_ir(shader->ir, state); + } + + /* Optimization passes */ + if (!state->error && !shader->ir->is_empty()) { + bool progress; + do { + progress = false; + + progress = do_mat_op_to_vec(shader->ir) || progress; + progress = do_vec_index_to_swizzle(shader->ir) || progress; + progress = do_vec_index_to_cond_assign(shader->ir) || progress; + } while (progress); + + validate_ir_tree(shader->ir); + } + + /* Print out the resulting IR */ + if (!state->error && dump_lir) { + _mesa_print_ir(shader->ir, state); + } + + shader->symbols = state->symbols; + shader->CompileStatus = !state->error; + shader->Version = state->language_version; + memcpy(shader->builtins_to_link, state->builtins_to_link, + sizeof(shader->builtins_to_link[0]) * state->num_builtins_to_link); + shader->num_builtins_to_link = state->num_builtins_to_link; + + if (shader->InfoLog) + talloc_free(shader->InfoLog); + + shader->InfoLog = state->info_log; + + /* Retain any live IR, but trash the rest. */ + reparent_ir(shader->ir, shader); + + talloc_free(state); + + return; +} + +int +main(int argc, char **argv) +{ + int status = EXIT_SUCCESS; + GLcontext local_ctx; + GLcontext *ctx = &local_ctx; + + ctx->Driver.NewShader = _mesa_new_shader; + + int c; + int idx = 0; + while ((c = getopt_long(argc, argv, "", compiler_opts, &idx)) != -1) + /* empty */ ; + + + if (argc <= optind) + usage_fail(argv[0]); + + struct gl_shader_program *whole_program; + + whole_program = talloc_zero (NULL, struct gl_shader_program); + assert(whole_program != NULL); + + for (/* empty */; argc > optind; optind++) { + whole_program->Shaders = (struct gl_shader **) + talloc_realloc(whole_program, whole_program->Shaders, + struct gl_shader *, whole_program->NumShaders + 1); + assert(whole_program->Shaders != NULL); + + struct gl_shader *shader = talloc_zero(whole_program, gl_shader); + + whole_program->Shaders[whole_program->NumShaders] = shader; + whole_program->NumShaders++; + + const unsigned len = strlen(argv[optind]); + if (len < 6) + usage_fail(argv[0]); + + const char *const ext = & argv[optind][len - 5]; + if (strncmp(".vert", ext, 5) == 0) + shader->Type = GL_VERTEX_SHADER; + else if (strncmp(".geom", ext, 5) == 0) + shader->Type = GL_GEOMETRY_SHADER; + else if (strncmp(".frag", ext, 5) == 0) + shader->Type = GL_FRAGMENT_SHADER; + else + usage_fail(argv[0]); + + shader->Source = load_text_file(whole_program, argv[optind]); + if (shader->Source == NULL) { + printf("File \"%s\" does not exist.\n", argv[optind]); + exit(EXIT_FAILURE); + } + + compile_shader(shader); + + if (!shader->CompileStatus) { + printf("Info log for %s:\n%s\n", argv[optind], shader->InfoLog); + status = EXIT_FAILURE; + break; + } + + llvm::Module* mod = glsl_ir_to_llvm_module(shader->ir); + if(!mod) + std::cerr << "Failed to translate " << argv[optind] << std::endl; + else + { + std::string out_file = std::string(argv[optind]) + ".bc"; + std::string err_info; + llvm::raw_fd_ostream os(out_file.c_str(), err_info, llvm::raw_fd_ostream::F_Binary); + llvm::WriteBitcodeToFile(mod, os); + delete mod; + + std::cerr << "Compiled " << argv[optind] << " to " << out_file << std::endl; + } + } + + if ((status == EXIT_SUCCESS) && do_link) { + link_shaders(ctx, whole_program); + status = (whole_program->LinkStatus) ? EXIT_SUCCESS : EXIT_FAILURE; + + if (strlen(whole_program->InfoLog) > 0) + printf("Info log for linking:\n%s\n", whole_program->InfoLog); + } + + if(status != EXIT_SUCCESS) + return status; + + for (unsigned i = 0; i < whole_program->_NumLinkedShaders; i++) + { + std::string type; + std::string long_type; + switch(whole_program->_LinkedShaders[i]->Type) + { + case GL_VERTEX_SHADER: + type = "vert"; + long_type = "vertex"; + break; + case GL_FRAGMENT_SHADER: + type = "frag"; + long_type = "fragment"; + break; + case GL_GEOMETRY_SHADER: + type = "geom"; + long_type = "geometry"; + break; + case GL_TESS_CONTROL_SHADER: + type = "tess"; + long_type = "tessellation control"; + break; + case GL_TESS_EVALUATION_SHADER: + type = "eval"; + long_type = "tessellation evaluation"; + break; + } + + llvm::Module* mod = glsl_ir_to_llvm_module(whole_program->_LinkedShaders[i]->ir); + if(!mod) + std::cerr << "Failed to translate linked " << long_type << " shaders" << std::endl; + else + { + std::string out_file = type + ".bc"; + std::string err_info; + llvm::raw_fd_ostream os(out_file.c_str(), err_info, llvm::raw_fd_ostream::F_Binary); + llvm::WriteBitcodeToFile(mod, os); + delete mod; + + std::cerr << "Linked " << long_type << " shaders into " << out_file << std::endl; + } + } + + for (unsigned i = 0; i < whole_program->_NumLinkedShaders; i++) + talloc_free(whole_program->_LinkedShaders[i]); + + talloc_free(whole_program); + _mesa_glsl_release_types(); + _mesa_glsl_release_functions(); + + std::cerr << "Use `opt` to optimize the bytecode the files, and `llvm-dis` to view them." << std::endl; + + return status; +} diff --git a/src/glsl/ir_to_llvm.cpp b/src/glsl/ir_to_llvm.cpp new file mode 100644 index 0000000000..b889c76415 --- /dev/null +++ b/src/glsl/ir_to_llvm.cpp @@ -0,0 +1,1117 @@ +/* + * Copyright (C) 2005-2007 Brian Paul All Rights Reserved. + * Copyright (C) 2008 VMware, Inc. All Rights Reserved. + * Copyright © 2010 Intel Corporation + * Copyright © 2010 Luca Barbieri + * + * 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. + */ + +/** + * \file ir_to_llvm.cpp + * + * Translates the IR to LLVM + */ + +/* this tends to get set as part of LLVM_CFLAGS, but we definitely want asserts */ +#ifdef NDEBUG +#undef NDEBUG +#endif + +#include "llvm/DerivedTypes.h" +#include "llvm/LLVMContext.h" +#include "llvm/Module.h" +#include "llvm/Analysis/Verifier.h" +#include "llvm/Support/IRBuilder.h" +#include "llvm/Intrinsics.h" + +#include <vector> +#include <stdio.h> +#ifdef _MSC_VER +#include <unordered_map> +#else +#include <tr1/unordered_map> +#endif +// use C++0x/Microsoft convention +namespace std +{ + using namespace tr1; +} + +#include "ir.h" +#include "ir_visitor.h" +#include "glsl_types.h" + +class ir_to_llvm_visitor : public ir_visitor { +public: + ir_to_llvm_visitor(); + + llvm::LLVMContext& ctx; + llvm::Module* mod; + llvm::Function* fun; + // could easily support more loops, but GLSL doesn't support multiloop break/continue + std::pair<llvm::BasicBlock*, llvm::BasicBlock*> loop; + llvm::BasicBlock* bb; + llvm::Value* result; + llvm::IRBuilder<> bld; + + ir_to_llvm_visitor(llvm::LLVMContext& p_ctx, llvm::Module* p_mod) + : ctx(p_ctx), mod(p_mod), fun(0), loop(std::make_pair((llvm::BasicBlock*)0, (llvm::BasicBlock*)0)), bb(0), bld(ctx) + { + } + + const llvm::Type* llvm_base_type(unsigned base_type) + { + switch(base_type) + { + case GLSL_TYPE_VOID: + return llvm::Type::getVoidTy(ctx); + case GLSL_TYPE_UINT: + case GLSL_TYPE_INT: + return llvm::Type::getInt32Ty(ctx); + case GLSL_TYPE_FLOAT: + return llvm::Type::getFloatTy(ctx); + case GLSL_TYPE_BOOL: + return llvm::Type::getInt1Ty(ctx); + case GLSL_TYPE_SAMPLER: + return llvm::PointerType::getUnqual(llvm::Type::getVoidTy(ctx)); + default: + assert(0); + return 0; + } + } + + const llvm::Type* llvm_vec_type(const glsl_type* type) + { + if(type->is_array()) + return llvm::ArrayType::get(llvm_type(type->fields.array), type->array_size()); + + if(type->is_record()) + { + std::vector<const llvm::Type*> fields; + for (unsigned i = 0; i < type->length; i++) + fields.push_back(llvm_type(type->fields.structure[i].type)); + return llvm::StructType::get(ctx, fields); + } + + const llvm::Type* base_type = llvm_base_type(type->base_type); + if(type->vector_elements <= 1) + return base_type; + else + return llvm::VectorType::get(base_type, type->vector_elements); + } + + const llvm::Type* llvm_type(const glsl_type* type) + { + const llvm::Type* vec_type = llvm_vec_type(type); + if(type->matrix_columns <= 1) + return vec_type; + else + return llvm::ArrayType::get(vec_type, type->matrix_columns); + } + + typedef std::unordered_map<ir_variable*, llvm::Value*> llvm_variables_t; + llvm_variables_t llvm_variables; + + llvm::Value* llvm_variable(class ir_variable* var) + { + llvm_variables_t::iterator vari = llvm_variables.find(var); + if(vari != llvm_variables.end()) + return vari->second; + else + { + const llvm::Type* type = llvm_type(var->type); + + llvm::Value* v; + if(fun) + { + if(bb == &fun->getEntryBlock()) + v = bld.CreateAlloca(type, 0, var->name); + else + v = new llvm::AllocaInst(type, 0, var->name, fun->getEntryBlock().getTerminator()); + } + else // TODO: can anything global be non-constant in GLSL?; fix linkage + { + llvm::Function::LinkageTypes linkage; + if(var->mode == ir_var_auto || var->mode == ir_var_temporary) + linkage = llvm::GlobalValue::InternalLinkage; + else + linkage = llvm::GlobalValue::ExternalLinkage; + llvm::Constant* init = 0; + if(var->constant_value) + init = llvm_constant(var->constant_value); + else if(linkage == llvm::GlobalValue::InternalLinkage) + init = llvm::UndefValue::get(llvm_type(var->type)); + v = new llvm::GlobalVariable(*mod, type, var->read_only, linkage, init, var->name); + } + llvm_variables[var] = v; + return v; + } + } + + typedef std::unordered_map<ir_function_signature*, llvm::Function*> llvm_functions_t; + llvm_functions_t llvm_functions; + + llvm::Function* llvm_function(class ir_function_signature* sig) + { + llvm_functions_t::iterator funi = llvm_functions.find(sig); + if(funi != llvm_functions.end()) + return funi->second; + else + { + const char* name = sig->function_name(); + llvm::Function::LinkageTypes linkage; + if(!strcmp(name, "main") || !sig->is_defined) + linkage = llvm::Function::ExternalLinkage; + else + linkage = llvm::Function::InternalLinkage; + std::vector<const llvm::Type*> params; + foreach_iter(exec_list_iterator, iter, sig->parameters) { + ir_variable* arg = (ir_variable*)iter.get(); + params.push_back(llvm_type(arg->type)); + } + + llvm::FunctionType* ft = llvm::FunctionType::get(llvm_type(sig->return_type), params, false); + + llvm::Function* f = llvm::Function::Create(ft, linkage, name, mod); + llvm_functions[sig] = f; + return f; + } + + } + + llvm::Value* llvm_value(class ir_instruction* ir) + { + result = 0; + ir->accept(this); + return result; + } + + llvm::Constant* llvm_constant(class ir_instruction* ir) + { + return &dynamic_cast<llvm::Constant&>(*llvm_value(ir)); + } + + llvm::Constant* llvm_int(unsigned v) + { + return llvm::ConstantInt::get(llvm::Type::getInt32Ty(ctx), v); + } + + llvm::Value* llvm_pointer(class ir_rvalue* ir) + { + if(ir_dereference_variable* deref = ir->as_dereference_variable()) + return llvm_variable(deref->variable_referenced()); + else if(ir_dereference_array* deref = ir->as_dereference_array()) + { + llvm::Value* gep[2] = {llvm_int(0), llvm_value(deref->array_index)}; + return bld.CreateInBoundsGEP(llvm_pointer(deref->array), gep, gep + 2); + } + else if(ir->as_dereference()) + { + ir_dereference_record* deref = (ir_dereference_record*)ir; + int idx = deref->record->type->field_index(deref->field); + assert(idx >= 0); + return bld.CreateConstInBoundsGEP2_32(llvm_pointer(deref->record), 0, idx); + } + else + { + assert(0); + return 0; + } + } + + llvm::Value* llvm_intrinsic(llvm::Intrinsic::ID id, llvm::Value* a) + { + const llvm::Type* types[1] = {a->getType()}; + return bld.CreateCall(llvm::Intrinsic::getDeclaration(mod, id, types, 1), a); + } + + llvm::Value* llvm_intrinsic(llvm::Intrinsic::ID id, llvm::Value* a, llvm::Value* b) + { + const llvm::Type* types[2] = {a->getType(), b->getType()}; + /* only one type suffix is usually needed, so pass 1 here */ + return bld.CreateCall2(llvm::Intrinsic::getDeclaration(mod, id, types, 1), a, b); + } + + llvm::Constant* llvm_imm(const llvm::Type* type, double v) + { + if(type->isVectorTy()) + { + std::vector<llvm::Constant*> values; + values.push_back(llvm_imm(((llvm::VectorType*)type)->getElementType(), v)); + for(unsigned i = 1; i < ((llvm::VectorType*)type)->getNumElements(); ++i) + values.push_back(values[0]); + return llvm::ConstantVector::get(values); + } + else if(type->isIntegerTy()) + return llvm::ConstantInt::get(type, v); + else if(type->isFloatingPointTy()) + return llvm::ConstantFP::get(type, v); + else + { + assert(0); + return 0; + } + } + + static llvm::Value* create_shuffle3(llvm::IRBuilder<>& bld, llvm::Value* v, unsigned a, unsigned b, unsigned c, const llvm::Twine& name = "") + { + const llvm::Type* int_ty = llvm::Type::getInt32Ty(v->getContext()); + llvm::Constant* vals[3] = {llvm::ConstantInt::get(int_ty, a), llvm::ConstantInt::get(int_ty, b), llvm::ConstantInt::get(int_ty, c)}; + return bld.CreateShuffleVector(v, llvm::UndefValue::get(v->getType()), llvm::ConstantVector::get(vals, 3), name); + } + + llvm::Value* llvm_expression(ir_expression* ir) + { + llvm::Value* ops[2]; + for(unsigned i = 0; i < ir->get_num_operands(); ++i) + ops[i] = llvm_value(ir->operands[i]); + + if(ir->get_num_operands() == 2) + { + int vecidx = -1; + int scaidx = -1; + if(ir->operands[0]->type->vector_elements <= 1 && ir->operands[1]->type->vector_elements > 1) + { + scaidx = 0; + vecidx = 1; + } + else if(ir->operands[0]->type->vector_elements > 1 && ir->operands[1]->type->vector_elements <= 1) + { + scaidx = 1; + vecidx = 0; + } + else + assert(ir->operands[0]->type->vector_elements == ir->operands[1]->type->vector_elements); + + if(scaidx >= 0) + { + llvm::Value* vec; + vec = llvm::UndefValue::get(ops[vecidx]->getType()); + for(unsigned i = 0; i < ir->operands[vecidx]->type->vector_elements; ++i) + vec = bld.CreateInsertElement(vec, ops[scaidx], llvm_int(i), "sca2vec"); + ops[scaidx] = vec; + } + } + + switch (ir->operation) { + case ir_unop_logic_not: + return bld.CreateNot(ops[0]); + case ir_unop_neg: + return bld.CreateNeg(ops[0]); + case ir_unop_abs: + switch(ir->operands[0]->type->base_type) + { + case GLSL_TYPE_UINT: + case GLSL_TYPE_BOOL: + return ops[0]; + case GLSL_TYPE_INT: + return bld.CreateSelect(bld.CreateICmpSGE(ops[0], llvm_imm(ops[0]->getType(), 0), "sabs.ge"), ops[0], bld.CreateNeg(ops[0], "sabs.neg"), "sabs.select"); + case GLSL_TYPE_FLOAT: + return bld.CreateSelect(bld.CreateFCmpUGE(ops[0], llvm_imm(ops[0]->getType(), 0), "fabs.ge"), ops[0], bld.CreateFNeg(ops[0], "fabs.neg"), "fabs.select"); + default: + assert(0); + } + case ir_unop_sign: + switch(ir->operands[0]->type->base_type) + { + case GLSL_TYPE_BOOL: + return ops[0]; + case GLSL_TYPE_UINT: + return bld.CreateZExt(bld.CreateICmpNE(ops[0], llvm_imm(ops[0]->getType(), 0), "usign.ne"), ops[0]->getType(), "usign.zext"); + case GLSL_TYPE_INT: + return bld.CreateSelect(bld.CreateICmpNE(ops[0], llvm_imm(ops[0]->getType(), 0), "ssign.ne"), + bld.CreateSelect(bld.CreateICmpSGE(ops[0], llvm_imm(ops[0]->getType(), 0), "ssign.ge"), llvm_imm(ops[0]->getType(), 1), llvm_imm(ops[0]->getType(), -1), "sabs.selects"), + llvm_imm(ops[0]->getType(), 0), "sabs.select0"); + case GLSL_TYPE_FLOAT: + return bld.CreateSelect(bld.CreateFCmpONE(ops[0], llvm_imm(ops[0]->getType(), 0), "fsign.ne"), + bld.CreateSelect(bld.CreateFCmpUGE(ops[0], llvm_imm(ops[0]->getType(), 0), "fsign.ge"), llvm_imm(ops[0]->getType(), 1), llvm_imm(ops[0]->getType(), -1), "fabs.selects"), + llvm_imm(ops[0]->getType(), 0), "fabs.select0"); + default: + assert(0); + } + case ir_unop_rcp: + assert(ir->operands[0]->type->base_type == GLSL_TYPE_FLOAT); + return bld.CreateFDiv(llvm_imm(ops[0]->getType(), 1), ops[0]); + case ir_unop_exp: + assert(ir->operands[0]->type->base_type == GLSL_TYPE_FLOAT); + return llvm_intrinsic(llvm::Intrinsic::exp, ops[0]); + case ir_unop_exp2: + assert(ir->operands[0]->type->base_type == GLSL_TYPE_FLOAT); + return llvm_intrinsic(llvm::Intrinsic::exp2, ops[0]); + case ir_unop_log: + assert(ir->operands[0]->type->base_type == GLSL_TYPE_FLOAT); + return llvm_intrinsic(llvm::Intrinsic::log, ops[0]); + case ir_unop_log2: + assert(ir->operands[0]->type->base_type == GLSL_TYPE_FLOAT); + return llvm_intrinsic(llvm::Intrinsic::log2, ops[0]); + case ir_unop_sin: + assert(ir->operands[0]->type->base_type == GLSL_TYPE_FLOAT); + return llvm_intrinsic(llvm::Intrinsic::sin, ops[0]); + case ir_unop_cos: + assert(ir->operands[0]->type->base_type == GLSL_TYPE_FLOAT); + return llvm_intrinsic(llvm::Intrinsic::cos, ops[0]); + // TODO: implement these somehow + case ir_unop_dFdx: + assert(0); + //return llvm_intrinsic(llvm::Intrinsic::ddx, ops[0]); + case ir_unop_dFdy: + assert(0); + //return llvm_intrinsic(llvm::Intrinsic::ddy, ops[0]); + case ir_binop_add: + switch(ir->operands[0]->type->base_type) + { + case GLSL_TYPE_BOOL: + case GLSL_TYPE_UINT: + case GLSL_TYPE_INT: + return bld.CreateAdd(ops[0], ops[1]); + case GLSL_TYPE_FLOAT: + return bld.CreateFAdd(ops[0], ops[1]); + default: + assert(0); + } + case ir_binop_sub: + switch(ir->operands[0]->type->base_type) + { + case GLSL_TYPE_BOOL: + case GLSL_TYPE_UINT: + case GLSL_TYPE_INT: + return bld.CreateSub(ops[0], ops[1]); + case GLSL_TYPE_FLOAT: + return bld.CreateFSub(ops[0], ops[1]); + default: + assert(0); + } + case ir_binop_mul: + switch(ir->operands[0]->type->base_type) + { + case GLSL_TYPE_BOOL: + return bld.CreateAnd(ops[0], ops[1]); + case GLSL_TYPE_UINT: + case GLSL_TYPE_INT: + return bld.CreateMul(ops[0], ops[1]); + case GLSL_TYPE_FLOAT: + return bld.CreateFMul(ops[0], ops[1]); + default: + assert(0); + } + case ir_binop_div: + switch(ir->operands[0]->type->base_type) + { + case GLSL_TYPE_BOOL: + case GLSL_TYPE_UINT: + return bld.CreateUDiv(ops[0], ops[1]); + case GLSL_TYPE_INT: + return bld.CreateSDiv(ops[0], ops[1]); + case GLSL_TYPE_FLOAT: + return bld.CreateFDiv(ops[0], ops[1]); + default: + assert(0); + } + case ir_binop_mod: + switch(ir->operands[0]->type->base_type) + { + case GLSL_TYPE_BOOL: + case GLSL_TYPE_UINT: + return bld.CreateURem(ops[0], ops[1]); + case GLSL_TYPE_INT: + return bld.CreateSRem(ops[0], ops[1]); + case GLSL_TYPE_FLOAT: + return bld.CreateFRem(ops[0], ops[1]); + default: + assert(0); + } + case ir_binop_less: + switch(ir->operands[0]->type->base_type) + { + case GLSL_TYPE_BOOL: + case GLSL_TYPE_UINT: + return bld.CreateICmpULT(ops[0], ops[1]); + case GLSL_TYPE_INT: + return bld.CreateICmpSLT(ops[0], ops[1]); + case GLSL_TYPE_FLOAT: + return bld.CreateFCmpOLT(ops[0], ops[1]); + default: + assert(0); + } + case ir_binop_greater: + switch(ir->operands[0]->type->base_type) + { + case GLSL_TYPE_BOOL: + case GLSL_TYPE_UINT: + return bld.CreateICmpUGT(ops[0], ops[1]); + case GLSL_TYPE_INT: + return bld.CreateICmpSGT(ops[0], ops[1]); + case GLSL_TYPE_FLOAT: + return bld.CreateFCmpOGT(ops[0], ops[1]); + default: + assert(0); + } + case ir_binop_lequal: + switch(ir->operands[0]->type->base_type) + { + case GLSL_TYPE_BOOL: + case GLSL_TYPE_UINT: + return bld.CreateICmpULE(ops[0], ops[1]); + case GLSL_TYPE_INT: + return bld.CreateICmpSLE(ops[0], ops[1]); + case GLSL_TYPE_FLOAT: + return bld.CreateFCmpOLE(ops[0], ops[1]); + default: + assert(0); + } + case ir_binop_gequal: + switch(ir->operands[0]->type->base_type) + { + case GLSL_TYPE_BOOL: + case GLSL_TYPE_UINT: + return bld.CreateICmpUGE(ops[0], ops[1]); + case GLSL_TYPE_INT: + return bld.CreateICmpSGE(ops[0], ops[1]); + case GLSL_TYPE_FLOAT: + return bld.CreateFCmpOGE(ops[0], ops[1]); + default: + assert(0); + } + case ir_binop_equal: + switch(ir->operands[0]->type->base_type) + { + case GLSL_TYPE_BOOL: + case GLSL_TYPE_UINT: + case GLSL_TYPE_INT: + return bld.CreateICmpEQ(ops[0], ops[1]); + case GLSL_TYPE_FLOAT: + return bld.CreateFCmpOEQ(ops[0], ops[1]); + default: + assert(0); + } + case ir_binop_nequal: + switch(ir->operands[0]->type->base_type) + { + case GLSL_TYPE_BOOL: + case GLSL_TYPE_UINT: + case GLSL_TYPE_INT: + return bld.CreateICmpNE(ops[0], ops[1]); + case GLSL_TYPE_FLOAT: + return bld.CreateFCmpONE(ops[0], ops[1]); + default: + assert(0); + } + case ir_binop_logic_xor: + assert(ir->operands[0]->type->base_type == GLSL_TYPE_BOOL); + return bld.CreateICmpNE(ops[0], ops[1]); + case ir_binop_logic_or: + assert(ir->operands[0]->type->base_type == GLSL_TYPE_BOOL); + return bld.CreateOr(ops[0], ops[1]); + case ir_binop_logic_and: + assert(ir->operands[0]->type->base_type == GLSL_TYPE_BOOL); + return bld.CreateAnd(ops[0], ops[1]); + case ir_binop_dot: + { + llvm::Value* prod; + switch(ir->operands[0]->type->base_type) + { + case GLSL_TYPE_UINT: + case GLSL_TYPE_INT: + prod = bld.CreateMul(ops[0], ops[1], "dot.mul"); + break; + case GLSL_TYPE_FLOAT: + prod = bld.CreateFMul(ops[0], ops[1], "dot.mul"); + break; + default: + assert(0); + } + + if(ir->operands[0]->type->vector_elements <= 1) + return prod; + + llvm::Value* sum = 0; + for(unsigned i = 0; i < ir->operands[0]->type->vector_elements; ++i) + { + llvm::Value* elem = bld.CreateExtractElement(prod, llvm_int(i), "dot.elem"); + if(sum) + { + if(ir->operands[0]->type->base_type == GLSL_TYPE_FLOAT) + sum = bld.CreateFAdd(sum, elem, "dot.add"); + else + sum = bld.CreateAdd(sum, elem, "dot.add"); + } + else + sum = elem; + } + return sum; + } + case ir_binop_cross: + assert(ir->operands[0]->type->vector_elements == 3); + switch(ir->operands[0]->type->base_type) + { + case GLSL_TYPE_UINT: + case GLSL_TYPE_INT: + return bld.CreateSub( + bld.CreateMul(create_shuffle3(bld, ops[0], 1, 2, 0, "cross.a120"), create_shuffle3(bld, ops[1], 2, 0, 1, "cross.a201"), "cross.ab"), + bld.CreateMul(create_shuffle3(bld, ops[1], 1, 2, 0, "cross.b120"), create_shuffle3(bld, ops[0], 2, 0, 1, "cross.b201"), "cross.ba"), + "cross.sub"); + case GLSL_TYPE_FLOAT: + return bld.CreateFSub( + bld.CreateFMul(create_shuffle3(bld, ops[0], 1, 2, 0, "cross.a120"), create_shuffle3(bld, ops[1], 2, 0, 1, "cross.a201"), "cross.ab"), + bld.CreateFMul(create_shuffle3(bld, ops[1], 1, 2, 0, "cross.b120"), create_shuffle3(bld, ops[0], 2, 0, 1, "cross.b201"), "cross.ba"), + "cross.sub"); + default: + assert(0); + } + case ir_unop_sqrt: + assert(ir->operands[0]->type->base_type == GLSL_TYPE_FLOAT); + return llvm_intrinsic(llvm::Intrinsic::sqrt, ops[0]); + case ir_unop_rsq: + assert(ir->operands[0]->type->base_type == GLSL_TYPE_FLOAT); + return bld.CreateFDiv(llvm_imm(ops[0]->getType(), 1), llvm_intrinsic(llvm::Intrinsic::sqrt, ops[0]), "rsqrt.rcp"); + case ir_unop_i2f: + return bld.CreateSIToFP(ops[0], llvm_type(ir->type)); + case ir_unop_u2f: + case ir_unop_b2f: + return bld.CreateUIToFP(ops[0], llvm_type(ir->type)); + case ir_unop_b2i: + return bld.CreateZExt(ops[0], llvm_type(ir->type)); + case ir_unop_f2i: + return bld.CreateFPToSI(ops[0], llvm_type(ir->type)); + case ir_unop_f2b: + return bld.CreateFCmpONE(ops[0], llvm_imm(ops[0]->getType(), 0)); + case ir_unop_i2b: + return bld.CreateICmpNE(ops[0], llvm_imm(ops[0]->getType(), 0)); + case ir_unop_trunc: + { + if(ir->operands[0]->type->base_type != GLSL_TYPE_FLOAT) + return ops[0]; + glsl_type int_type = *ir->operands[0]->type; + int_type.base_type = GLSL_TYPE_INT; + return bld.CreateSIToFP(bld.CreateFPToSI(ops[0], llvm_type(&int_type), "trunc.fptosi"),ops[0]->getType(), "trunc.sitofp"); + } + case ir_unop_floor: + { + if(ir->operands[0]->type->base_type != GLSL_TYPE_FLOAT) + return ops[0]; + llvm::Value* one = llvm_imm(ops[0]->getType(), 1); + return bld.CreateFSub(ops[0], bld.CreateFRem(ops[0], one)); + } + case ir_unop_ceil: + { + if(ir->operands[0]->type->base_type != GLSL_TYPE_FLOAT) + return ops[0]; + llvm::Value* one = llvm_imm(ops[0]->getType(), 1); + return bld.CreateFAdd(bld.CreateFSub(ops[0], bld.CreateFRem(ops[0], one)), one); + } + case ir_unop_fract: + { + if(ir->operands[0]->type->base_type != GLSL_TYPE_FLOAT) + return llvm_imm(ops[0]->getType(), 0); + llvm::Value* one = llvm_imm(ops[0]->getType(), 1); + return bld.CreateFRem(ops[0], one); + } + // TODO: NaNs might be wrong in min/max, not sure how to fix it + case ir_binop_min: + switch(ir->operands[0]->type->base_type) + { + case GLSL_TYPE_BOOL: + return bld.CreateAnd(ops[0], ops[1], "bmin"); + case GLSL_TYPE_UINT: + return bld.CreateSelect(bld.CreateICmpULE(ops[0], ops[1], "umin.le"), ops[0], ops[1], "umin.select"); + case GLSL_TYPE_INT: + return bld.CreateSelect(bld.CreateICmpSLE(ops[0], ops[1], "smin.le"), ops[0], ops[1], "smin.select"); + case GLSL_TYPE_FLOAT: + return bld.CreateSelect(bld.CreateFCmpULE(ops[0], ops[1], "fmin.le"), ops[0], ops[1], "fmin.select"); + default: + assert(0); + } + case ir_binop_max: + switch(ir->operands[0]->type->base_type) + { + case GLSL_TYPE_BOOL: + return bld.CreateOr(ops[0], ops[1], "bmax"); + case GLSL_TYPE_UINT: + return bld.CreateSelect(bld.CreateICmpUGE(ops[0], ops[1], "umax.ge"), ops[0], ops[1], "umax.select"); + case GLSL_TYPE_INT: + return bld.CreateSelect(bld.CreateICmpSGE(ops[0], ops[1], "smax.ge"), ops[0], ops[1], "smax.select"); + case GLSL_TYPE_FLOAT: + return bld.CreateSelect(bld.CreateFCmpUGE(ops[0], ops[1], "fmax.ge"), ops[0], ops[1], "fmax.select"); + default: + assert(0); + } + case ir_binop_pow: + return llvm_intrinsic(llvm::Intrinsic::pow, ops[0], ops[1]); + break; + case ir_unop_bit_not: + return bld.CreateNot(ops[0]); + case ir_binop_bit_and: + return bld.CreateAnd(ops[0], ops[1]); + case ir_binop_bit_xor: + return bld.CreateXor(ops[0], ops[1]); + case ir_binop_bit_or: + return bld.CreateOr(ops[0], ops[1]); + case ir_binop_lshift: + switch(ir->operands[0]->type->base_type) + { + case GLSL_TYPE_BOOL: + case GLSL_TYPE_UINT: + case GLSL_TYPE_INT: + return bld.CreateLShr(ops[0], ops[1]); + default: + assert(0); + } + case ir_binop_rshift: + switch(ir->operands[0]->type->base_type) + { + case GLSL_TYPE_BOOL: + case GLSL_TYPE_UINT: + return bld.CreateLShr(ops[0], ops[1]); + case GLSL_TYPE_INT: + return bld.CreateAShr(ops[0], ops[1]); + default: + assert(0); + return 0; + } + default: + assert(0); + return 0; + } + } + + virtual void visit(class ir_expression * ir) + { + result = llvm_expression(ir); + } + + virtual void visit(class ir_dereference_array *ir) + { + result = bld.CreateLoad(llvm_pointer(ir)); + } + + virtual void visit(class ir_dereference_record *ir) + { + result = bld.CreateLoad(llvm_pointer(ir)); + } + + virtual void visit(class ir_dereference_variable *ir) + { + result = bld.CreateLoad(llvm_pointer(ir)); + } + + virtual void visit(class ir_texture * ir) + { + // TODO + } + + virtual void visit(class ir_discard * ir) + { + llvm::BasicBlock* discard = llvm::BasicBlock::Create(ctx, "discard", fun); + llvm::BasicBlock* after; + if(ir->condition) + { + after = llvm::BasicBlock::Create(ctx, "discard.survived", fun); + bld.CreateCondBr(llvm_value(ir->condition), discard, after); + } + else + { + after = llvm::BasicBlock::Create(ctx, "dead_code.discard", fun); + bld.CreateBr(discard); + } + + bld.SetInsertPoint(discard); + bld.CreateUnwind(); + + bb = after; + bld.SetInsertPoint(bb); + } + + virtual void visit(class ir_loop_jump *ir) + { + llvm::BasicBlock* target; + if(ir->mode == ir_loop_jump::jump_continue) + target = loop.first; + else if(ir->mode == ir_loop_jump::jump_break) + target = loop.second; + assert(target); + + bld.CreateBr(target); + + bb = llvm::BasicBlock::Create(ctx, "dead_code.jump", fun); + bld.SetInsertPoint(bb); + } + + virtual void visit(class ir_loop * ir) + { + llvm::BasicBlock* body = llvm::BasicBlock::Create(ctx, "loop", fun); + llvm::BasicBlock* header = body; + llvm::BasicBlock* after = llvm::BasicBlock::Create(ctx, "loop.after", fun); + llvm::Value* ctr; + + if(ir->counter) + { + ctr = llvm_variable(ir->counter); + if(ir->from) + bld.CreateStore(llvm_value(ir->from), ctr); + if(ir->to) + header = llvm::BasicBlock::Create(ctx, "loop.header", fun); + } + + bld.CreateBr(header); + + if(ir->counter && ir->to) + { + bld.SetInsertPoint(header); + llvm::Value* cond; + llvm::Value* load = bld.CreateLoad(ctr); + llvm::Value* to = llvm_value(ir->to); + switch(ir->counter->type->base_type) + { + case GLSL_TYPE_BOOL: + case GLSL_TYPE_UINT: + cond = bld.CreateICmpULT(load, to); + break; + case GLSL_TYPE_INT: + cond = bld.CreateICmpSLT(load, to); + break; + case GLSL_TYPE_FLOAT: + cond = bld.CreateFCmpOLT(load, to); + break; + } + bld.CreateCondBr(cond, body, after); + } + + bld.SetInsertPoint(body); + + std::pair<llvm::BasicBlock*, llvm::BasicBlock*> saved_loop = loop; + loop = std::make_pair(header, after); + visit_exec_list(&ir->body_instructions, this); + loop = saved_loop; + + if(ir->counter && ir->increment) + { + switch(ir->counter->type->base_type) + { + case GLSL_TYPE_BOOL: + case GLSL_TYPE_UINT: + case GLSL_TYPE_INT: + bld.CreateStore(bld.CreateAdd(bld.CreateLoad(ctr), llvm_value(ir->increment)), ctr); + break; + case GLSL_TYPE_FLOAT: + bld.CreateStore(bld.CreateFAdd(bld.CreateLoad(ctr), llvm_value(ir->increment)), ctr); + break; + } + } + bld.CreateBr(header); + + bb = after; + bld.SetInsertPoint(bb); + } + + virtual void visit(class ir_if *ir) + { + llvm::BasicBlock* bbt = llvm::BasicBlock::Create(ctx, "if", fun); + llvm::BasicBlock* bbf = llvm::BasicBlock::Create(ctx, "else", fun); + llvm::BasicBlock* bbe = llvm::BasicBlock::Create(ctx, "endif", fun); + bld.CreateCondBr(llvm_value(ir->condition), bbt, bbf); + + bld.SetInsertPoint(bbt); + visit_exec_list(&ir->then_instructions, this); + bld.CreateBr(bbe); + + bld.SetInsertPoint(bbf); + visit_exec_list(&ir->else_instructions, this); + bld.CreateBr(bbe); + + bb = bbe; + bld.SetInsertPoint(bb); + } + + virtual void visit(class ir_return * ir) + { + if(!ir->value) + bld.CreateRetVoid(); + else + bld.CreateRet(llvm_value(ir->value)); + + bb = llvm::BasicBlock::Create(ctx, "dead_code.return", fun); + bld.SetInsertPoint(bb); + } + + virtual void visit(class ir_call * ir) + { + std::vector<llvm::Value*> args; + + foreach_iter(exec_list_iterator, iter, *ir) + { + ir_rvalue *arg = (ir_constant *)iter.get(); + args.push_back(llvm_value(arg)); + } + + result = bld.CreateCall(llvm_function(ir->get_callee()), args.begin(), args.end()); + + llvm::AttrListPtr attr; + ((llvm::CallInst*)result)->setAttributes(attr); + } + + virtual void visit(class ir_constant * ir) + { + if (ir->type->base_type == GLSL_TYPE_STRUCT) { + std::vector<llvm::Constant*> fields; + foreach_iter(exec_list_iterator, iter, ir->components) { + ir_constant *field = (ir_constant *)iter.get(); + fields.push_back(llvm_constant(field)); + } + result = llvm::ConstantStruct::get((llvm::StructType*)llvm_type(ir->type), fields); + } + else if (ir->type->base_type == GLSL_TYPE_ARRAY) { + std::vector<llvm::Constant*> elems; + for (unsigned i = 0; i < ir->type->length; i++) + elems.push_back(llvm_constant(ir->array_elements[i])); + result = llvm::ConstantArray::get((llvm::ArrayType*)llvm_type(ir->type), elems); + } + else + { + const llvm::Type* base_type = llvm_base_type(ir->type->base_type); + const llvm::Type* vec_type = llvm_vec_type(ir->type); + const llvm::Type* type = llvm_type(ir->type); + + std::vector<llvm::Constant*> vecs; + unsigned idx = 0; + for (unsigned i = 0; i < ir->type->matrix_columns; ++i) { + std::vector<llvm::Constant*> elems; + for (unsigned j = 0; j < ir->type->vector_elements; ++j) { + llvm::Constant* elem; + switch(ir->type->base_type) + { + case GLSL_TYPE_FLOAT: + elem = llvm::ConstantFP::get(base_type, ir->value.f[idx]); + break; + case GLSL_TYPE_UINT: + elem = llvm::ConstantInt::get(base_type, ir->value.u[idx]); + break; + case GLSL_TYPE_INT: + elem = llvm::ConstantInt::get(base_type, ir->value.i[idx]); + break; + case GLSL_TYPE_BOOL: + elem = llvm::ConstantInt::get(base_type, ir->value.b[idx]); + break; + } + elems.push_back(elem); + ++idx; + } + + llvm::Constant* vec; + if(ir->type->vector_elements > 1) + vec = llvm::ConstantVector::get((llvm::VectorType*)vec_type, elems); + else + vec = elems[0]; + vecs.push_back(vec); + } + + if(ir->type->matrix_columns > 1) + result = llvm::ConstantArray::get((llvm::ArrayType*)type, vecs); + else + result = vecs[0]; + } + } + + llvm::Value* llvm_shuffle(llvm::Value* val, int* shuffle_mask, unsigned res_width, const llvm::Twine &name = "") + { + const llvm::Type* elem_type = val->getType(); + const llvm::Type* res_type = elem_type;; + unsigned val_width = 1; + if(val->getType()->isVectorTy()) + { + val_width = ((llvm::VectorType*)val->getType())->getNumElements(); + elem_type = ((llvm::VectorType*)val->getType())->getElementType(); + } + if(res_width > 1) + res_type = llvm::VectorType::get(elem_type, res_width); + + llvm::Constant* shuffle_mask_values[4]; + assert(res_width <= 4); + bool any_def = false; + for(unsigned i = 0; i < res_width; ++i) + { + if(shuffle_mask[i] < 0) + shuffle_mask_values[i] = llvm::UndefValue::get(llvm::Type::getInt32Ty(ctx)); + else + { + any_def = true; + shuffle_mask_values[i] = llvm_int(shuffle_mask[i]); + } + } + + llvm::Value* undef = llvm::UndefValue::get(res_type); + if(!any_def) + return undef; + + if(val_width > 1) + { + if(res_width > 1) + { + if(val_width == res_width) + { + bool nontrivial = false; + for(unsigned i = 0; i < val_width; ++i) + { + if(shuffle_mask[i] != (int)i) + nontrivial = true; + } + if(!nontrivial) + return val; + } + + return bld.CreateShuffleVector(val, llvm::UndefValue::get(val->getType()), llvm::ConstantVector::get(shuffle_mask_values, res_width), name); + } + else + return bld.CreateExtractElement(val, llvm_int(shuffle_mask[0]), name); + } + else + { + if(res_width > 1) + { + llvm::Value* tmp = undef; + for(unsigned i = 0; i < res_width; ++i) + { + if(shuffle_mask[i] >= 0) + tmp = bld.CreateInsertElement(tmp, val, llvm_int(i), name); + } + return tmp; + } + else if(shuffle_mask[0] >= 0) + return val; + else + return undef; + } + } + + + virtual void visit(class ir_swizzle * swz) + { + llvm::Value* val = llvm_value(swz->val); + int mask[4] = {swz->mask.x, swz->mask.y, swz->mask.z, swz->mask.w}; + result = llvm_shuffle(val, mask, swz->mask.num_components, "swizzle"); + } + + virtual void visit(class ir_assignment * ir) + { + llvm::Value* lhs = llvm_pointer(ir->lhs); + llvm::Value* rhs = llvm_value(ir->rhs); + unsigned width = ir->lhs->type->vector_elements; + unsigned mask = (1 << width) - 1; + assert(rhs); + + if(!(ir->write_mask & mask)) + return; + + if(ir->rhs->type->vector_elements < width) + { + int expand_mask[4] = {-1, -1, -1, -1}; + for(unsigned i = 0; i < ir->rhs->type->vector_elements; ++i) + expand_mask[i] = i; +// printf("ve: %u w %u issw: %i\n", ir->rhs->type->vector_elements, width, !!ir->rhs->as_swizzle()); + rhs = llvm_shuffle(rhs, expand_mask, width, "assign.expand"); + } + + if(width > 1 && (ir->write_mask & mask) != mask) + { + llvm::Constant* blend_mask[4]; + for(unsigned i = 0; i < width; ++i) + { + if(ir->write_mask & (1 << i)) + blend_mask[i] = llvm_int(width + i); + else + blend_mask[i] = llvm_int(i); + } + rhs = bld.CreateShuffleVector(bld.CreateLoad(lhs), rhs, llvm::ConstantVector::get(blend_mask, width), "assign.writemask"); + } + + if(ir->condition) + rhs = bld.CreateSelect(llvm_value(ir->condition), rhs, bld.CreateLoad(lhs), "assign.conditional"); + + bld.CreateStore(rhs, lhs); + } + + virtual void visit(class ir_variable * var) + { + llvm_variable(var); + } + + virtual void visit(ir_function_signature *sig) + { + if(!sig->is_defined) + return; + + assert(!fun); + fun = llvm_function(sig); + + bb = llvm::BasicBlock::Create(ctx, "entry", fun); + bld.SetInsertPoint(bb); + + llvm::Function::arg_iterator ai = fun->arg_begin(); + foreach_iter(exec_list_iterator, iter, sig->parameters) { + ir_variable* arg = (ir_variable*)iter.get(); + ai->setName(arg->name); + bld.CreateStore(ai, llvm_variable(arg)); + ++ai; + } + + foreach_iter(exec_list_iterator, iter, sig->body) { + ir_instruction *ir = (ir_instruction *)iter.get(); + + ir->accept(this); + } + + if(fun->getReturnType()->isVoidTy()) + bld.CreateRetVoid(); + else + bld.CreateRet(llvm::UndefValue::get(fun->getReturnType())); + + bb = NULL; + fun = NULL; + } + + virtual void visit(class ir_function * funs) + { + foreach_iter(exec_list_iterator, iter, *funs) + { + ir_function_signature* sig = (ir_function_signature*)iter.get(); + sig->accept(this); + } + } +}; + +struct llvm::Module * +glsl_ir_to_llvm_module(struct exec_list *ir) +{ + llvm::LLVMContext& ctx = llvm::getGlobalContext(); + llvm::Module* mod = new llvm::Module("glsl", ctx); + ir_to_llvm_visitor v(ctx, mod); + + visit_exec_list(ir, &v); + +// mod->dump(); + if(llvm::verifyModule(*mod, llvm::PrintMessageAction, 0)) + { + delete mod; + return 0; + } + + return mod; + //v.ir_to_llvm_emit_op1(NULL, OPCODE_END, ir_to_llvm_undef_dst, ir_to_llvm_undef); +} diff --git a/src/glsl/ir_to_llvm.h b/src/glsl/ir_to_llvm.h new file mode 100644 index 0000000000..64acf19aab --- /dev/null +++ b/src/glsl/ir_to_llvm.h @@ -0,0 +1,9 @@ +#ifndef IR_TO_LLVM_H_ +#define IR_TO_LLVM_H_ + +#include "llvm/Module.h" +#include "ir.h" + +struct llvm::Module * glsl_ir_to_llvm_module(struct exec_list *ir); + +#endif /* IR_TO_LLVM_H_ */ |