diff options
Diffstat (limited to 'src')
-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_ */ |