Path: blob/21.2-virgl/src/gallium/auxiliary/gallivm/lp_bld_ir_common.c
4565 views
/**************************************************************************1*2* Copyright 2009 VMware, Inc.3* Copyright 2007-2008 VMware, Inc.4* All Rights Reserved.5*6* Permission is hereby granted, free of charge, to any person obtaining a7* copy of this software and associated documentation files (the8* "Software"), to deal in the Software without restriction, including9* without limitation the rights to use, copy, modify, merge, publish,10* distribute, sub license, and/or sell copies of the Software, and to11* permit persons to whom the Software is furnished to do so, subject to12* the following conditions:13*14* The above copyright notice and this permission notice (including the15* next paragraph) shall be included in all copies or substantial portions16* of the Software.17*18* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS19* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF20* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.21* IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR22* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,23* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE24* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.25*26**************************************************************************/2728#include "util/u_memory.h"29#include "lp_bld_type.h"30#include "lp_bld_init.h"31#include "lp_bld_flow.h"32#include "lp_bld_ir_common.h"33#include "lp_bld_logic.h"3435/*36* Return the context for the current function.37* (always 'main', if shader doesn't do any function calls)38*/39static inline struct function_ctx *40func_ctx(struct lp_exec_mask *mask)41{42assert(mask->function_stack_size > 0);43assert(mask->function_stack_size <= LP_MAX_NUM_FUNCS);44return &mask->function_stack[mask->function_stack_size - 1];45}4647/*48* Returns true if we're in a loop.49* It's global, meaning that it returns true even if there's50* no loop inside the current function, but we were inside51* a loop inside another function, from which this one was called.52*/53static inline boolean54mask_has_loop(struct lp_exec_mask *mask)55{56int i;57for (i = mask->function_stack_size - 1; i >= 0; --i) {58const struct function_ctx *ctx = &mask->function_stack[i];59if (ctx->loop_stack_size > 0)60return TRUE;61}62return FALSE;63}6465/*66* Returns true if we're inside a switch statement.67* It's global, meaning that it returns true even if there's68* no switch in the current function, but we were inside69* a switch inside another function, from which this one was called.70*/71static inline boolean72mask_has_switch(struct lp_exec_mask *mask)73{74int i;75for (i = mask->function_stack_size - 1; i >= 0; --i) {76const struct function_ctx *ctx = &mask->function_stack[i];77if (ctx->switch_stack_size > 0)78return TRUE;79}80return FALSE;81}8283/*84* Returns true if we're inside a conditional.85* It's global, meaning that it returns true even if there's86* no conditional in the current function, but we were inside87* a conditional inside another function, from which this one was called.88*/89static inline boolean90mask_has_cond(struct lp_exec_mask *mask)91{92int i;93for (i = mask->function_stack_size - 1; i >= 0; --i) {94const struct function_ctx *ctx = &mask->function_stack[i];95if (ctx->cond_stack_size > 0)96return TRUE;97}98return FALSE;99}100101void lp_exec_mask_update(struct lp_exec_mask *mask)102{103LLVMBuilderRef builder = mask->bld->gallivm->builder;104boolean has_loop_mask = mask_has_loop(mask);105boolean has_cond_mask = mask_has_cond(mask);106boolean has_switch_mask = mask_has_switch(mask);107boolean has_ret_mask = mask->function_stack_size > 1 ||108mask->ret_in_main;109110if (has_loop_mask) {111/*for loops we need to update the entire mask at runtime */112LLVMValueRef tmp;113assert(mask->break_mask);114tmp = LLVMBuildAnd(builder,115mask->cont_mask,116mask->break_mask,117"maskcb");118mask->exec_mask = LLVMBuildAnd(builder,119mask->cond_mask,120tmp,121"maskfull");122} else123mask->exec_mask = mask->cond_mask;124125if (has_switch_mask) {126mask->exec_mask = LLVMBuildAnd(builder,127mask->exec_mask,128mask->switch_mask,129"switchmask");130}131132if (has_ret_mask) {133mask->exec_mask = LLVMBuildAnd(builder,134mask->exec_mask,135mask->ret_mask,136"callmask");137}138139mask->has_mask = (has_cond_mask ||140has_loop_mask ||141has_switch_mask ||142has_ret_mask);143}144145/*146* Initialize a function context at the specified index.147*/148void149lp_exec_mask_function_init(struct lp_exec_mask *mask, int function_idx)150{151LLVMTypeRef int_type = LLVMInt32TypeInContext(mask->bld->gallivm->context);152LLVMBuilderRef builder = mask->bld->gallivm->builder;153struct function_ctx *ctx = &mask->function_stack[function_idx];154155ctx->cond_stack_size = 0;156ctx->loop_stack_size = 0;157ctx->bgnloop_stack_size = 0;158ctx->switch_stack_size = 0;159160if (function_idx == 0) {161ctx->ret_mask = mask->ret_mask;162}163164ctx->loop_limiter = lp_build_alloca(mask->bld->gallivm,165int_type, "looplimiter");166LLVMBuildStore(167builder,168LLVMConstInt(int_type, LP_MAX_TGSI_LOOP_ITERATIONS, false),169ctx->loop_limiter);170}171172void lp_exec_mask_init(struct lp_exec_mask *mask, struct lp_build_context *bld)173{174mask->bld = bld;175mask->has_mask = FALSE;176mask->ret_in_main = FALSE;177/* For the main function */178mask->function_stack_size = 1;179180mask->int_vec_type = lp_build_int_vec_type(bld->gallivm, mask->bld->type);181mask->exec_mask = mask->ret_mask = mask->break_mask = mask->cont_mask =182mask->cond_mask = mask->switch_mask =183LLVMConstAllOnes(mask->int_vec_type);184185mask->function_stack = CALLOC(LP_MAX_NUM_FUNCS,186sizeof(mask->function_stack[0]));187lp_exec_mask_function_init(mask, 0);188}189190void191lp_exec_mask_fini(struct lp_exec_mask *mask)192{193FREE(mask->function_stack);194}195196/* stores val into an address pointed to by dst_ptr.197* mask->exec_mask is used to figure out which bits of val198* should be stored into the address199* (0 means don't store this bit, 1 means do store).200*/201void lp_exec_mask_store(struct lp_exec_mask *mask,202struct lp_build_context *bld_store,203LLVMValueRef val,204LLVMValueRef dst_ptr)205{206LLVMBuilderRef builder = mask->bld->gallivm->builder;207LLVMValueRef exec_mask = mask->has_mask ? mask->exec_mask : NULL;208209assert(lp_check_value(bld_store->type, val));210assert(LLVMGetTypeKind(LLVMTypeOf(dst_ptr)) == LLVMPointerTypeKind);211assert(LLVMGetElementType(LLVMTypeOf(dst_ptr)) == LLVMTypeOf(val) ||212LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(dst_ptr))) == LLVMArrayTypeKind);213214if (exec_mask) {215LLVMValueRef res, dst;216217dst = LLVMBuildLoad(builder, dst_ptr, "");218if (bld_store->type.width < 32)219exec_mask = LLVMBuildTrunc(builder, exec_mask, bld_store->vec_type, "");220res = lp_build_select(bld_store, exec_mask, val, dst);221LLVMBuildStore(builder, res, dst_ptr);222} else223LLVMBuildStore(builder, val, dst_ptr);224}225226void lp_exec_bgnloop_post_phi(struct lp_exec_mask *mask)227{228LLVMBuilderRef builder = mask->bld->gallivm->builder;229struct function_ctx *ctx = func_ctx(mask);230231if (ctx->loop_stack_size != ctx->bgnloop_stack_size) {232mask->break_mask = LLVMBuildLoad(builder, ctx->break_var, "");233lp_exec_mask_update(mask);234ctx->bgnloop_stack_size = ctx->loop_stack_size;235}236}237238void lp_exec_bgnloop(struct lp_exec_mask *mask, bool load)239{240LLVMBuilderRef builder = mask->bld->gallivm->builder;241struct function_ctx *ctx = func_ctx(mask);242243if (ctx->loop_stack_size >= LP_MAX_TGSI_NESTING) {244++ctx->loop_stack_size;245return;246}247248ctx->break_type_stack[ctx->loop_stack_size + ctx->switch_stack_size] =249ctx->break_type;250ctx->break_type = LP_EXEC_MASK_BREAK_TYPE_LOOP;251252ctx->loop_stack[ctx->loop_stack_size].loop_block = ctx->loop_block;253ctx->loop_stack[ctx->loop_stack_size].cont_mask = mask->cont_mask;254ctx->loop_stack[ctx->loop_stack_size].break_mask = mask->break_mask;255ctx->loop_stack[ctx->loop_stack_size].break_var = ctx->break_var;256++ctx->loop_stack_size;257258ctx->break_var = lp_build_alloca(mask->bld->gallivm, mask->int_vec_type, "");259LLVMBuildStore(builder, mask->break_mask, ctx->break_var);260261ctx->loop_block = lp_build_insert_new_block(mask->bld->gallivm, "bgnloop");262263LLVMBuildBr(builder, ctx->loop_block);264LLVMPositionBuilderAtEnd(builder, ctx->loop_block);265266if (load) {267lp_exec_bgnloop_post_phi(mask);268}269}270271void lp_exec_endloop(struct gallivm_state *gallivm,272struct lp_exec_mask *mask)273{274LLVMBuilderRef builder = mask->bld->gallivm->builder;275struct function_ctx *ctx = func_ctx(mask);276LLVMBasicBlockRef endloop;277LLVMTypeRef int_type = LLVMInt32TypeInContext(mask->bld->gallivm->context);278LLVMTypeRef reg_type = LLVMIntTypeInContext(gallivm->context,279mask->bld->type.width *280mask->bld->type.length);281LLVMValueRef i1cond, i2cond, icond, limiter;282283assert(mask->break_mask);284285assert(ctx->loop_stack_size);286if (ctx->loop_stack_size > LP_MAX_TGSI_NESTING) {287--ctx->loop_stack_size;288--ctx->bgnloop_stack_size;289return;290}291292/*293* Restore the cont_mask, but don't pop294*/295mask->cont_mask = ctx->loop_stack[ctx->loop_stack_size - 1].cont_mask;296lp_exec_mask_update(mask);297298/*299* Unlike the continue mask, the break_mask must be preserved across loop300* iterations301*/302LLVMBuildStore(builder, mask->break_mask, ctx->break_var);303304/* Decrement the loop limiter */305limiter = LLVMBuildLoad(builder, ctx->loop_limiter, "");306307limiter = LLVMBuildSub(308builder,309limiter,310LLVMConstInt(int_type, 1, false),311"");312313LLVMBuildStore(builder, limiter, ctx->loop_limiter);314315/* i1cond = (mask != 0) */316i1cond = LLVMBuildICmp(317builder,318LLVMIntNE,319LLVMBuildBitCast(builder, mask->exec_mask, reg_type, ""),320LLVMConstNull(reg_type), "i1cond");321322/* i2cond = (looplimiter > 0) */323i2cond = LLVMBuildICmp(324builder,325LLVMIntSGT,326limiter,327LLVMConstNull(int_type), "i2cond");328329/* if( i1cond && i2cond ) */330icond = LLVMBuildAnd(builder, i1cond, i2cond, "");331332endloop = lp_build_insert_new_block(mask->bld->gallivm, "endloop");333334LLVMBuildCondBr(builder,335icond, ctx->loop_block, endloop);336337LLVMPositionBuilderAtEnd(builder, endloop);338339assert(ctx->loop_stack_size);340--ctx->loop_stack_size;341--ctx->bgnloop_stack_size;342mask->cont_mask = ctx->loop_stack[ctx->loop_stack_size].cont_mask;343mask->break_mask = ctx->loop_stack[ctx->loop_stack_size].break_mask;344ctx->loop_block = ctx->loop_stack[ctx->loop_stack_size].loop_block;345ctx->break_var = ctx->loop_stack[ctx->loop_stack_size].break_var;346ctx->break_type = ctx->break_type_stack[ctx->loop_stack_size +347ctx->switch_stack_size];348349lp_exec_mask_update(mask);350}351352void lp_exec_mask_cond_push(struct lp_exec_mask *mask,353LLVMValueRef val)354{355LLVMBuilderRef builder = mask->bld->gallivm->builder;356struct function_ctx *ctx = func_ctx(mask);357358if (ctx->cond_stack_size >= LP_MAX_TGSI_NESTING) {359ctx->cond_stack_size++;360return;361}362if (ctx->cond_stack_size == 0 && mask->function_stack_size == 1) {363assert(mask->cond_mask == LLVMConstAllOnes(mask->int_vec_type));364}365ctx->cond_stack[ctx->cond_stack_size++] = mask->cond_mask;366assert(LLVMTypeOf(val) == mask->int_vec_type);367mask->cond_mask = LLVMBuildAnd(builder,368mask->cond_mask,369val,370"");371lp_exec_mask_update(mask);372}373374void lp_exec_mask_cond_invert(struct lp_exec_mask *mask)375{376LLVMBuilderRef builder = mask->bld->gallivm->builder;377struct function_ctx *ctx = func_ctx(mask);378LLVMValueRef prev_mask;379LLVMValueRef inv_mask;380381assert(ctx->cond_stack_size);382if (ctx->cond_stack_size >= LP_MAX_TGSI_NESTING)383return;384prev_mask = ctx->cond_stack[ctx->cond_stack_size - 1];385if (ctx->cond_stack_size == 1 && mask->function_stack_size == 1) {386assert(prev_mask == LLVMConstAllOnes(mask->int_vec_type));387}388389inv_mask = LLVMBuildNot(builder, mask->cond_mask, "");390391mask->cond_mask = LLVMBuildAnd(builder,392inv_mask,393prev_mask, "");394lp_exec_mask_update(mask);395}396397void lp_exec_mask_cond_pop(struct lp_exec_mask *mask)398{399struct function_ctx *ctx = func_ctx(mask);400assert(ctx->cond_stack_size);401--ctx->cond_stack_size;402if (ctx->cond_stack_size >= LP_MAX_TGSI_NESTING)403return;404mask->cond_mask = ctx->cond_stack[ctx->cond_stack_size];405lp_exec_mask_update(mask);406}407408409void lp_exec_continue(struct lp_exec_mask *mask)410{411LLVMBuilderRef builder = mask->bld->gallivm->builder;412LLVMValueRef exec_mask = LLVMBuildNot(builder,413mask->exec_mask,414"");415416mask->cont_mask = LLVMBuildAnd(builder,417mask->cont_mask,418exec_mask, "");419420lp_exec_mask_update(mask);421}422423void lp_exec_break(struct lp_exec_mask *mask, int *pc,424bool break_always)425{426LLVMBuilderRef builder = mask->bld->gallivm->builder;427struct function_ctx *ctx = func_ctx(mask);428429if (ctx->break_type == LP_EXEC_MASK_BREAK_TYPE_LOOP) {430LLVMValueRef exec_mask = LLVMBuildNot(builder,431mask->exec_mask,432"break");433434mask->break_mask = LLVMBuildAnd(builder,435mask->break_mask,436exec_mask, "break_full");437}438else {439if (ctx->switch_in_default) {440/*441* stop default execution but only if this is an unconditional switch.442* (The condition here is not perfect since dead code after break is443* allowed but should be sufficient since false negatives are just444* unoptimized - so we don't have to pre-evaluate that).445*/446if(break_always && ctx->switch_pc) {447if (pc)448*pc = ctx->switch_pc;449return;450}451}452453if (break_always) {454mask->switch_mask = LLVMConstNull(mask->bld->int_vec_type);455}456else {457LLVMValueRef exec_mask = LLVMBuildNot(builder,458mask->exec_mask,459"break");460mask->switch_mask = LLVMBuildAnd(builder,461mask->switch_mask,462exec_mask, "break_switch");463}464}465466lp_exec_mask_update(mask);467}468469470