From ced73ea5df306156e5383b438d6858e58b1479e5 Mon Sep 17 00:00:00 2001 From: Tom Stellard Date: Tue, 17 Apr 2012 09:26:28 -0400 Subject: r600g: Add hooks for the LLVM shader compiler The LLVM backend can now be enabled for r600g by using the --enable-r600-llvm-compiler configure flag. If you configure with this flag, you can still use the default compiler by setting the envrionment variable R600_USE_LLVM=0 Reviewed-by: Alex Deucher --- configure.ac | 14 ++ src/gallium/drivers/r600/Makefile.am | 24 +++ src/gallium/drivers/r600/Makefile.sources | 2 + src/gallium/drivers/r600/r600_shader.c | 280 +++++++++++++++++++++++++++++- 4 files changed, 318 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 54009581878..6776886fef1 100644 --- a/configure.ac +++ b/configure.ac @@ -637,6 +637,12 @@ AC_ARG_ENABLE([gallium_gbm], [enable_gallium_gbm="$enableval"], [enable_gallium_gbm=auto]) +AC_ARG_ENABLE([r600-llvm-compiler], + [AS_HELP_STRING([--enable-r600-llvm-compilerl], + [Enable experimental LLVM backend for graphics shaders @<:default=disable@:>@])], + [enable_r600_llvm="$enableval"], + [enable_r600_llvm=no]) + # Option for Gallium drivers GALLIUM_DRIVERS_DEFAULT="r300,r600,svga,swrast" @@ -1906,6 +1912,13 @@ if test "x$with_gallium_drivers" != x; then xr600) PKG_CHECK_MODULES([RADEON], [libdrm_radeon >= $LIBDRM_RADEON_REQUIRED]) GALLIUM_DRIVERS_DIRS="$GALLIUM_DRIVERS_DIRS r600" + if test "x$enable_r600_llvm" = xyes; then + if test "x$LLVM_VERSION" != "x3.1"; then + AC_MSG_ERROR([LLVM 3.1 is required for the r600 llvm compiler.]) + fi + NEED_RADEON_GALLIUM=yes; + USE_R600_LLVM_COMPILER=yes; + fi gallium_check_st "radeon/drm" "dri-r600" "xorg-r600" "" "xvmc-r600" "vdpau-r600" "va-r600" ;; xradeonsi) @@ -1976,6 +1989,7 @@ AM_CONDITIONAL(HAVE_GALAHAD_GALLIUM, test x$HAVE_GALAHAD_GALLIUM = xyes) AM_CONDITIONAL(HAVE_IDENTITY_GALLIUM, test x$HAVE_IDENTITY_GALLIUM = xyes) AM_CONDITIONAL(HAVE_NOOP_GALLIUM, test x$HAVE_NOOP_GALLIUM = xyes) AM_CONDITIONAL(NEED_RADEON_GALLIUM, test x$NEED_RADEON_GALLIUM = xyes) +AM_CONDITIONAL(USE_R600_LLVM_COMPILER, test x$USE_R600_LLVM_COMPILER = xyes) AC_SUBST([GALLIUM_MAKE_DIRS]) dnl prepend CORE_DIRS to SRC_DIRS diff --git a/src/gallium/drivers/r600/Makefile.am b/src/gallium/drivers/r600/Makefile.am index 8acd36ac865..a567f07ac8d 100644 --- a/src/gallium/drivers/r600/Makefile.am +++ b/src/gallium/drivers/r600/Makefile.am @@ -15,3 +15,27 @@ AM_CFLAGS = \ libr600_a_SOURCES = \ $(C_SOURCES) + +if USE_R600_LLVM_COMPILER + +# This is a hack until we can move the backend into the LLVM project. +# We need to use mklib, because it splits up libradeon.a into object files +# so that we can link it with the r600 objects. +libr600_a_AR = $(top_srcdir)/bin/mklib -o r600 -static + +libr600_a_SOURCES += \ + $(LLVM_C_SOURCES) + +libr600_a_LIBADD = \ + $(top_srcdir)/src/gallium/drivers/radeon/libradeon.a + +AM_CFLAGS += \ + $(LLVM_CFLAGS) \ + -I$(top_srcdir)/src/gallium/drivers/radeon/ \ + -DR600_USE_LLVM + +AM_CXXFLAGS= \ + $(LLVM_CXXFLAGS) +else +libr600_a_AR = $(AR) $(ARFLAGS) +endif diff --git a/src/gallium/drivers/r600/Makefile.sources b/src/gallium/drivers/r600/Makefile.sources index e7813ef51c8..a920b930be8 100644 --- a/src/gallium/drivers/r600/Makefile.sources +++ b/src/gallium/drivers/r600/Makefile.sources @@ -15,3 +15,5 @@ C_SOURCES := \ eg_asm.c \ r600_translate.c \ r600_state_common.c + +LLVM_C_SOURCES := r600_llvm.c diff --git a/src/gallium/drivers/r600/r600_shader.c b/src/gallium/drivers/r600/r600_shader.c index 9d85de0f8ec..b594b28380a 100644 --- a/src/gallium/drivers/r600/r600_shader.c +++ b/src/gallium/drivers/r600/r600_shader.c @@ -21,14 +21,18 @@ * USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "r600_sq.h" +#include "r600_llvm.h" #include "r600_formats.h" #include "r600_opcodes.h" #include "r600d.h" +#include "pipe/p_shader_tokens.h" #include "tgsi/tgsi_info.h" #include "tgsi/tgsi_parse.h" #include "tgsi/tgsi_scan.h" #include "tgsi/tgsi_dump.h" +#include "util/u_memory.h" +#include #include #include @@ -206,6 +210,224 @@ struct r600_shader_tgsi_instruction { static struct r600_shader_tgsi_instruction r600_shader_tgsi_instruction[], eg_shader_tgsi_instruction[], cm_shader_tgsi_instruction[]; static int tgsi_helper_tempx_replicate(struct r600_shader_ctx *ctx); +static inline void callstack_check_depth(struct r600_shader_ctx *ctx, unsigned reason, unsigned check_max_only); +static void fc_pushlevel(struct r600_shader_ctx *ctx, int type); +static int tgsi_else(struct r600_shader_ctx *ctx); +static int tgsi_endif(struct r600_shader_ctx *ctx); +static int tgsi_bgnloop(struct r600_shader_ctx *ctx); +static int tgsi_endloop(struct r600_shader_ctx *ctx); +static int tgsi_loop_brk_cont(struct r600_shader_ctx *ctx); + +/* + * bytestream -> r600 shader + * + * These functions are used to transform the output of the LLVM backend into + * struct r600_bytecode. + */ + +static unsigned r600_src_from_byte_stream(unsigned char * bytes, + unsigned bytes_read, struct r600_bytecode_alu * alu, unsigned src_idx) +{ + unsigned i; + unsigned sel0, sel1; + sel0 = bytes[bytes_read++]; + sel1 = bytes[bytes_read++]; + alu->src[src_idx].sel = sel0 | (sel1 << 8); + alu->src[src_idx].chan = bytes[bytes_read++]; + alu->src[src_idx].neg = bytes[bytes_read++]; + alu->src[src_idx].abs = bytes[bytes_read++]; + alu->src[src_idx].rel = bytes[bytes_read++]; + alu->src[src_idx].kc_bank = bytes[bytes_read++]; + for (i = 0; i < 4; i++) { + alu->src[src_idx].value |= bytes[bytes_read++] << (i * 8); + } + return bytes_read; +} + +static unsigned r600_alu_from_byte_stream(struct r600_shader_ctx *ctx, + unsigned char * bytes, unsigned bytes_read) +{ + unsigned src_idx; + unsigned inst0, inst1; + struct r600_bytecode_alu alu; + memset(&alu, 0, sizeof(alu)); + for(src_idx = 0; src_idx < 3; src_idx++) { + bytes_read = r600_src_from_byte_stream(bytes, bytes_read, + &alu, src_idx); + } + + alu.dst.sel = bytes[bytes_read++]; + alu.dst.chan = bytes[bytes_read++]; + alu.dst.clamp = bytes[bytes_read++]; + alu.dst.write = bytes[bytes_read++]; + alu.dst.rel = bytes[bytes_read++]; + inst0 = bytes[bytes_read++]; + inst1 = bytes[bytes_read++]; + alu.inst = inst0 | (inst1 << 8); + alu.last = bytes[bytes_read++]; + alu.is_op3 = bytes[bytes_read++]; + alu.predicate = bytes[bytes_read++]; + alu.bank_swizzle = bytes[bytes_read++]; + alu.bank_swizzle_force = bytes[bytes_read++]; + alu.omod = bytes[bytes_read++]; + alu.index_mode = bytes[bytes_read++]; + r600_bytecode_add_alu(ctx->bc, &alu); + + /* XXX: Handle other KILL instructions */ + if (alu.inst == CTX_INST(V_SQ_ALU_WORD1_OP2_SQ_OP2_INST_KILLGT)) { + ctx->shader->uses_kill = 1; + /* XXX: This should be enforced in the LLVM backend. */ + ctx->bc->force_add_cf = 1; + } + return bytes_read; +} + +static void llvm_if(struct r600_shader_ctx *ctx, struct r600_bytecode_alu * alu, + unsigned pred_inst) +{ + alu->inst = pred_inst; + alu->predicate = 1; + alu->src[1].sel = V_SQ_ALU_SRC_0; + alu->src[1].chan = 0; + alu->last = 1; + r600_bytecode_add_alu_type(ctx->bc, alu, + CTX_INST(V_SQ_CF_ALU_WORD1_SQ_CF_INST_ALU_PUSH_BEFORE)); + + r600_bytecode_add_cfinst(ctx->bc, CTX_INST(V_SQ_CF_WORD1_SQ_CF_INST_JUMP)); + fc_pushlevel(ctx, FC_IF); + callstack_check_depth(ctx, FC_PUSH_VPM, 0); +} + +static void r600_break_from_byte_stream(struct r600_shader_ctx *ctx, + struct r600_bytecode_alu *alu, unsigned compare_opcode) +{ + unsigned opcode = TGSI_OPCODE_BRK; + if (ctx->bc->chip_class == CAYMAN) + ctx->inst_info = &cm_shader_tgsi_instruction[opcode]; + else if (ctx->bc->chip_class >= EVERGREEN) + ctx->inst_info = &eg_shader_tgsi_instruction[opcode]; + else + ctx->inst_info = &r600_shader_tgsi_instruction[opcode]; + llvm_if(ctx, alu, compare_opcode); + tgsi_loop_brk_cont(ctx); + tgsi_endif(ctx); +} + +static unsigned r600_fc_from_byte_stream(struct r600_shader_ctx *ctx, + unsigned char * bytes, unsigned bytes_read) +{ + struct r600_bytecode_alu alu; + unsigned inst; + memset(&alu, 0, sizeof(alu)); + bytes_read = r600_src_from_byte_stream(bytes, bytes_read, &alu, 0); + inst = bytes[bytes_read++]; + switch (inst) { + case 0: + llvm_if(ctx, &alu, + CTX_INST(V_SQ_ALU_WORD1_OP2_SQ_OP2_INST_PRED_SETNE)); + break; + case 1: + tgsi_else(ctx); + break; + case 2: + tgsi_endif(ctx); + break; + case 3: + tgsi_bgnloop(ctx); + break; + case 4: + tgsi_endloop(ctx); + break; + case 5: + r600_break_from_byte_stream(ctx, &alu, + CTX_INST(V_SQ_ALU_WORD1_OP2_SQ_OP2_INST_PRED_SETE)); + break; + case 6: + r600_break_from_byte_stream(ctx, &alu, + CTX_INST(V_SQ_ALU_WORD1_OP2_SQ_OP2_INST_PRED_SETNE_INT)); + break; + case 7: + { + unsigned opcode = TGSI_OPCODE_CONT; + if (ctx->bc->chip_class == CAYMAN) { + ctx->inst_info = + &cm_shader_tgsi_instruction[opcode]; + } else if (ctx->bc->chip_class >= EVERGREEN) { + ctx->inst_info = + &eg_shader_tgsi_instruction[opcode]; + } else { + ctx->inst_info = + &r600_shader_tgsi_instruction[opcode]; + } + tgsi_loop_brk_cont(ctx); + } + break; + } + + return bytes_read; +} + +static unsigned r600_tex_from_byte_stream(struct r600_shader_ctx *ctx, + unsigned char * bytes, unsigned bytes_read) +{ + struct r600_bytecode_tex tex; + + tex.inst = bytes[bytes_read++]; + tex.resource_id = bytes[bytes_read++]; + tex.src_gpr = bytes[bytes_read++]; + tex.src_rel = bytes[bytes_read++]; + tex.dst_gpr = bytes[bytes_read++]; + tex.dst_rel = bytes[bytes_read++]; + tex.dst_sel_x = bytes[bytes_read++]; + tex.dst_sel_y = bytes[bytes_read++]; + tex.dst_sel_z = bytes[bytes_read++]; + tex.dst_sel_w = bytes[bytes_read++]; + tex.lod_bias = bytes[bytes_read++]; + tex.coord_type_x = bytes[bytes_read++]; + tex.coord_type_y = bytes[bytes_read++]; + tex.coord_type_z = bytes[bytes_read++]; + tex.coord_type_w = bytes[bytes_read++]; + tex.offset_x = bytes[bytes_read++]; + tex.offset_y = bytes[bytes_read++]; + tex.offset_z = bytes[bytes_read++]; + tex.sampler_id = bytes[bytes_read++]; + tex.src_sel_x = bytes[bytes_read++]; + tex.src_sel_y = bytes[bytes_read++]; + tex.src_sel_z = bytes[bytes_read++]; + tex.src_sel_w = bytes[bytes_read++]; + + r600_bytecode_add_tex(ctx->bc, &tex); + + return bytes_read; +} + +static void r600_bytecode_from_byte_stream(struct r600_shader_ctx *ctx, + unsigned char * bytes, unsigned num_bytes) +{ + unsigned bytes_read = 0; + while (bytes_read < num_bytes) { + char inst_type = bytes[bytes_read++]; + switch (inst_type) { + case 0: + bytes_read = r600_alu_from_byte_stream(ctx, bytes, + bytes_read); + break; + case 1: + bytes_read = r600_tex_from_byte_stream(ctx, bytes, + bytes_read); + break; + case 2: + bytes_read = r600_fc_from_byte_stream(ctx, bytes, + bytes_read); + break; + default: + /* XXX: Error here */ + break; + } + } +} + +/* End bytestream -> r600 shader functions*/ static int tgsi_is_supported(struct r600_shader_ctx *ctx) { @@ -818,7 +1040,14 @@ static int r600_shader_from_tgsi(struct r600_context * rctx, struct r600_pipe_sh unsigned opcode; int i, j, k, r = 0; int next_pixel_base = 0, next_pos_base = 60, next_param_base = 0; + /* Declarations used by llvm code */ + bool use_llvm = false; + unsigned char * inst_bytes; + unsigned inst_byte_count; +#ifdef R600_USE_LLVM + use_llvm = debug_get_bool_option("R600_LLVM", TRUE); +#endif ctx.bc = &shader->bc; ctx.shader = shader; ctx.native_integers = (rctx->screen->glsl_feature_level >= 130); @@ -874,8 +1103,46 @@ static int r600_shader_from_tgsi(struct r600_context * rctx, struct r600_pipe_sh if (ctx.type == TGSI_PROCESSOR_FRAGMENT && ctx.bc->chip_class >= EVERGREEN) { ctx.file_offset[TGSI_FILE_INPUT] = evergreen_gpr_count(&ctx); } - ctx.file_offset[TGSI_FILE_OUTPUT] = ctx.file_offset[TGSI_FILE_INPUT] + - ctx.info.file_max[TGSI_FILE_INPUT] + 1; + + /* LLVM backend setup */ +#ifdef R600_USE_LLVM + if (use_llvm && ctx.info.indirect_files) { + fprintf(stderr, "Warning: R600 LLVM backend does not support " + "indirect adressing. Falling back to TGSI " + "backend.\n"); + use_llvm = 0; + } + if (use_llvm) { + struct radeon_llvm_context radeon_llvm_ctx; + LLVMModuleRef mod; + unsigned dump = 0; + memset(&radeon_llvm_ctx, 0, sizeof(radeon_llvm_ctx)); + radeon_llvm_ctx.reserved_reg_count = ctx.file_offset[TGSI_FILE_INPUT]; + mod = r600_tgsi_llvm(&radeon_llvm_ctx, tokens); + if (debug_get_bool_option("R600_DUMP_SHADERS", FALSE)) { + dump = 1; + } + if (r600_llvm_compile(mod, &inst_bytes, &inst_byte_count, + rctx->family, dump)) { + FREE(inst_bytes); + radeon_llvm_dispose(&radeon_llvm_ctx); + use_llvm = 0; + fprintf(stderr, "R600 LLVM backend failed to compile " + "shader. Falling back to TGSI\n"); + } else { + ctx.file_offset[TGSI_FILE_OUTPUT] = + ctx.file_offset[TGSI_FILE_INPUT]; + } + radeon_llvm_dispose(&radeon_llvm_ctx); + } +#endif + /* End of LLVM backend setup */ + + if (!use_llvm) { + ctx.file_offset[TGSI_FILE_OUTPUT] = + ctx.file_offset[TGSI_FILE_INPUT] + + ctx.info.file_max[TGSI_FILE_INPUT] + 1; + } ctx.file_offset[TGSI_FILE_TEMPORARY] = ctx.file_offset[TGSI_FILE_OUTPUT] + ctx.info.file_max[TGSI_FILE_OUTPUT] + 1; @@ -976,6 +1243,9 @@ static int r600_shader_from_tgsi(struct r600_context * rctx, struct r600_pipe_sh tgsi_parse_token(&ctx.parse); switch (ctx.parse.FullToken.Token.Type) { case TGSI_TOKEN_TYPE_INSTRUCTION: + if (use_llvm) { + continue; + } r = tgsi_is_supported(&ctx); if (r) goto out_err; @@ -1003,6 +1273,12 @@ static int r600_shader_from_tgsi(struct r600_context * rctx, struct r600_pipe_sh } } + /* Get instructions if we are using the LLVM backend. */ + if (use_llvm) { + r600_bytecode_from_byte_stream(&ctx, inst_bytes, inst_byte_count); + FREE(inst_bytes); + } + noutput = shader->noutput; if (ctx.clip_vertex_write) { -- cgit v1.2.3