/************************************************************************** * * Copyright 2009 VMware, Inc. * All Rights Reserved. * * 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, sub license, 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 NON-INFRINGEMENT. * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS 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. * **************************************************************************/ /** * LLVM control flow build helpers. * * @author Jose Fonseca */ #include "util/u_debug.h" #include "util/u_memory.h" #include "lp_bld_type.h" #include "lp_bld_flow.h" #define LP_BUILD_FLOW_MAX_VARIABLES 32 #define LP_BUILD_FLOW_MAX_DEPTH 32 #define LP_BUILD_IF_MAX_VARIABLES 8 /** * Enumeration of all possible flow constructs. */ enum lp_build_flow_construct_kind { LP_BUILD_FLOW_SCOPE, LP_BUILD_FLOW_SKIP, LP_BUILD_FLOW_IF }; /** * Variable declaration scope. */ struct lp_build_flow_scope { /** Number of variables declared in this scope */ unsigned num_variables; }; /** * Early exit. Useful to skip to the end of a function or block when * the execution mask becomes zero or when there is an error condition. */ struct lp_build_flow_skip { /** Block to skip to */ LLVMBasicBlockRef block; /** Number of variables declared at the beginning */ unsigned num_variables; LLVMValueRef *phi; /**< array [num_variables] */ }; /** * if/else/endif. */ struct lp_build_flow_if { unsigned num_variables; LLVMValueRef *phi; /**< array [num_variables] */ LLVMValueRef condition; LLVMBasicBlockRef entry_block, true_block, false_block, merge_block; }; /** * Union of all possible flow constructs' data */ union lp_build_flow_construct_data { struct lp_build_flow_scope scope; struct lp_build_flow_skip skip; struct lp_build_flow_if ifthen; }; /** * Element of the flow construct stack. */ struct lp_build_flow_construct { enum lp_build_flow_construct_kind kind; union lp_build_flow_construct_data data; }; /** * All necessary data to generate LLVM control flow constructs. * * Besides keeping track of the control flow construct themselves we also * need to keep track of variables in order to generate SSA Phi values. */ struct lp_build_flow_context { LLVMBuilderRef builder; /** * Control flow stack. */ struct lp_build_flow_construct constructs[LP_BUILD_FLOW_MAX_DEPTH]; unsigned num_constructs; /** * Variable stack */ LLVMValueRef *variables[LP_BUILD_FLOW_MAX_VARIABLES]; unsigned num_variables; }; struct lp_build_flow_context * lp_build_flow_create(LLVMBuilderRef builder) { struct lp_build_flow_context *flow; flow = CALLOC_STRUCT(lp_build_flow_context); if(!flow) return NULL; flow->builder = builder; return flow; } void lp_build_flow_destroy(struct lp_build_flow_context *flow) { assert(flow->num_constructs == 0); assert(flow->num_variables == 0); FREE(flow); } /** * Begin/push a new flow control construct, such as a loop, skip block * or variable scope. */ static union lp_build_flow_construct_data * lp_build_flow_push(struct lp_build_flow_context *flow, enum lp_build_flow_construct_kind kind) { assert(flow->num_constructs < LP_BUILD_FLOW_MAX_DEPTH); if(flow->num_constructs >= LP_BUILD_FLOW_MAX_DEPTH) return NULL; flow->constructs[flow->num_constructs].kind = kind; return &flow->constructs[flow->num_constructs++].data; } /** * Return the current/top flow control construct on the stack. * \param kind the expected type of the top-most construct */ static union lp_build_flow_construct_data * lp_build_flow_peek(struct lp_build_flow_context *flow, enum lp_build_flow_construct_kind kind) { assert(flow->num_constructs); if(!flow->num_constructs) return NULL; assert(flow->constructs[flow->num_constructs - 1].kind == kind); if(flow->constructs[flow->num_constructs - 1].kind != kind) return NULL; return &flow->constructs[flow->num_constructs - 1].data; } /** * End/pop the current/top flow control construct on the stack. * \param kind the expected type of the top-most construct */ static union lp_build_flow_construct_data * lp_build_flow_pop(struct lp_build_flow_context *flow, enum lp_build_flow_construct_kind kind) { assert(flow->num_constructs); if(!flow->num_constructs) return NULL; assert(flow->constructs[flow->num_constructs - 1].kind == kind); if(flow->constructs[flow->num_constructs - 1].kind != kind) return NULL; return &flow->constructs[--flow->num_constructs].data; } /** * Begin a variable scope. * * */ void lp_build_flow_scope_begin(struct lp_build_flow_context *flow) { struct lp_build_flow_scope *scope; scope = &lp_build_flow_push(flow, LP_BUILD_FLOW_SCOPE)->scope; if(!scope) return; scope->num_variables = 0; } /** * Declare a variable. * * A variable is a named entity which can have different LLVMValueRef's at * different points of the program. This is relevant for control flow because * when there are multiple branches to a same location we need to replace * the variable's value with a Phi function as explained in * http://en.wikipedia.org/wiki/Static_single_assignment_form . * * We keep track of variables by keeping around a pointer to where they're * current. * * There are a few cautions to observe: * * - Variable's value must not be NULL. If there is no initial value then * LLVMGetUndef() should be used. * * - Variable's value must be kept up-to-date. If the variable is going to be * modified by a function then a pointer should be passed so that its value * is accurate. Failure to do this will cause some of the variables' * transient values to be lost, leading to wrong results. * * - A program should be written from top to bottom, by always appending * instructions to the bottom with a single LLVMBuilderRef. Inserting and/or * modifying existing statements will most likely lead to wrong results. * */ void lp_build_flow_scope_declare(struct lp_build_flow_context *flow, LLVMValueRef *variable) { struct lp_build_flow_scope *scope; scope = &lp_build_flow_peek(flow, LP_BUILD_FLOW_SCOPE)->scope; if(!scope) return; assert(*variable); if(!*variable) return; assert(flow->num_variables < LP_BUILD_FLOW_MAX_VARIABLES); if(flow->num_variables >= LP_BUILD_FLOW_MAX_VARIABLES) return; flow->variables[flow->num_variables++] = variable; ++scope->num_variables; } void lp_build_flow_scope_end(struct lp_build_flow_context *flow) { struct lp_build_flow_scope *scope; scope = &lp_build_flow_pop(flow, LP_BUILD_FLOW_SCOPE)->scope; if(!scope) return; assert(flow->num_variables >= scope->num_variables); if(flow->num_variables < scope->num_variables) { flow->num_variables = 0; return; } flow->num_variables -= scope->num_variables; } /** * Note: this function has no dependencies on the flow code and could * be used elsewhere. */ static LLVMBasicBlockRef lp_build_insert_new_block(LLVMBuilderRef builder, const char *name) { LLVMBasicBlockRef current_block; LLVMBasicBlockRef next_block; LLVMBasicBlockRef new_block; /* get current basic block */ current_block = LLVMGetInsertBlock(builder); /* check if there's another block after this one */ next_block = LLVMGetNextBasicBlock(current_block); if (next_block) { /* insert the new block before the next block */ new_block = LLVMInsertBasicBlock(next_block, name); } else { /* append new block after current block */ LLVMValueRef function = LLVMGetBasicBlockParent(current_block); new_block = LLVMAppendBasicBlock(function, name); } return new_block; } static LLVMBasicBlockRef lp_build_flow_insert_block(struct lp_build_flow_context *flow) { return lp_build_insert_new_block(flow->builder, ""); } /** * Begin a "skip" block. Inside this block we can test a condition and * skip to the end of the block if the condition is false. */ void lp_build_flow_skip_begin(struct lp_build_flow_context *flow) { struct lp_build_flow_skip *skip; LLVMBuilderRef builder; unsigned i; skip = &lp_build_flow_push(flow, LP_BUILD_FLOW_SKIP)->skip; if(!skip) return; /* create new basic block */ skip->block = lp_build_flow_insert_block(flow); skip->num_variables = flow->num_variables; if(!skip->num_variables) { skip->phi = NULL; return; } /* Allocate a Phi node for each variable in this skip scope */ skip->phi = MALLOC(skip->num_variables * sizeof *skip->phi); if(!skip->phi) { skip->num_variables = 0; return; } builder = LLVMCreateBuilder(); LLVMPositionBuilderAtEnd(builder, skip->block); /* create a Phi node for each variable */ for(i = 0; i < skip->num_variables; ++i) skip->phi[i] = LLVMBuildPhi(builder, LLVMTypeOf(*flow->variables[i]), ""); LLVMDisposeBuilder(builder); } /** * Insert code to test a condition and branch to the end of the current * skip block if the condition is true. */ void lp_build_flow_skip_cond_break(struct lp_build_flow_context *flow, LLVMValueRef cond) { struct lp_build_flow_skip *skip; LLVMBasicBlockRef current_block; LLVMBasicBlockRef new_block; unsigned i; skip = &lp_build_flow_peek(flow, LP_BUILD_FLOW_SKIP)->skip; if(!skip) return; current_block = LLVMGetInsertBlock(flow->builder); new_block = lp_build_flow_insert_block(flow); /* for each variable, update the Phi node with a (variable, block) pair */ for(i = 0; i < skip->num_variables; ++i) { assert(*flow->variables[i]); LLVMAddIncoming(skip->phi[i], flow->variables[i], ¤t_block, 1); } /* if cond is true, goto skip->block, else goto new_block */ LLVMBuildCondBr(flow->builder, cond, skip->block, new_block); LLVMPositionBuilderAtEnd(flow->builder, new_block); } void lp_build_flow_skip_end(struct lp_build_flow_context *flow) { struct lp_build_flow_skip *skip; LLVMBasicBlockRef current_block; unsigned i; skip = &lp_build_flow_pop(flow, LP_BUILD_FLOW_SKIP)->skip; if(!skip) return; current_block = LLVMGetInsertBlock(flow->builder); /* add (variable, block) tuples to the phi nodes */ for(i = 0; i < skip->num_variables; ++i) { assert(*flow->variables[i]); LLVMAddIncoming(skip->phi[i], flow->variables[i], ¤t_block, 1); *flow->variables[i] = skip->phi[i]; } /* goto block */ LLVMBuildBr(flow->builder, skip->block); LLVMPositionBuilderAtEnd(flow->builder, skip->block); FREE(skip->phi); } /** * Check if the mask predicate is zero. If so, jump to the end of the block. */ static void lp_build_mask_check(struct lp_build_mask_context *mask) { LLVMBuilderRef builder = mask->flow->builder; LLVMValueRef cond; /* cond = (mask == 0) */ cond = LLVMBuildICmp(builder, LLVMIntEQ, LLVMBuildBitCast(builder, mask->value, mask->reg_type, ""), LLVMConstNull(mask->reg_type), ""); /* if cond, goto end of block */ lp_build_flow_skip_cond_break(mask->flow, cond); } /** * Begin a section of code which is predicated on a mask. * \param mask the mask context, initialized here * \param flow the flow context * \param type the type of the mask * \param value storage for the mask */ void lp_build_mask_begin(struct lp_build_mask_context *mask, struct lp_build_flow_context *flow, struct lp_type type, LLVMValueRef value) { memset(mask, 0, sizeof *mask); mask->flow = flow; mask->reg_type = LLVMIntType(type.width * type.length); mask->value = value; lp_build_flow_scope_begin(flow); lp_build_flow_scope_declare(flow, &mask->value); lp_build_flow_skip_begin(flow); lp_build_mask_check(mask); } /** * Update boolean mask with given value (bitwise AND). * Typically used to update the quad's pixel alive/killed mask * after depth testing, alpha testing, TGSI_OPCODE_KIL, etc. */ void lp_build_mask_update(struct lp_build_mask_context *mask, LLVMValueRef value) { mask->value = LLVMBuildAnd( mask->flow->builder, mask->value, value, ""); lp_build_mask_check(mask); } /** * End section of code which is predicated on a mask. */ LLVMValueRef lp_build_mask_end(struct lp_build_mask_context *mask) { lp_build_flow_skip_end(mask->flow); lp_build_flow_scope_end(mask->flow); return mask->value; } void lp_build_loop_begin(LLVMBuilderRef builder, LLVMValueRef start, struct lp_build_loop_state *state) { LLVMBasicBlockRef block = LLVMGetInsertBlock(builder); LLVMValueRef function = LLVMGetBasicBlockParent(block); state->block = LLVMAppendBasicBlock(function, "loop"); LLVMBuildBr(builder, state->block); LLVMPositionBuilderAtEnd(builder, state->block); state->counter = LLVMBuildPhi(builder, LLVMTypeOf(start), ""); LLVMAddIncoming(state->counter, &start, &block, 1); } void lp_build_loop_end(LLVMBuilderRef builder, LLVMValueRef end, LLVMValueRef step, struct lp_build_loop_state *state) { LLVMBasicBlockRef block = LLVMGetInsertBlock(builder); LLVMValueRef function = LLVMGetBasicBlockParent(block); LLVMValueRef next; LLVMValueRef cond; LLVMBasicBlockRef after_block; if (!step) step = LLVMConstInt(LLVMTypeOf(end), 1, 0); next = LLVMBuildAdd(builder, state->counter, step, ""); cond = LLVMBuildICmp(builder, LLVMIntNE, next, end, ""); after_block = LLVMAppendBasicBlock(function, ""); LLVMBuildCondBr(builder, cond, after_block, state->block); LLVMAddIncoming(state->counter, &next, &block, 1); LLVMPositionBuilderAtEnd(builder, after_block); } /* Example of if/then/else building: int x; if (cond) { x = 1 + 2; } else { x = 2 + 3; } Is built with: LLVMValueRef x = LLVMGetUndef(); // or something else flow = lp_build_flow_create(builder); lp_build_flow_scope_begin(flow); // x needs a phi node lp_build_flow_scope_declare(flow, &x); lp_build_if(ctx, flow, builder, cond); x = LLVMAdd(1, 2); lp_build_else(ctx); x = LLVMAdd(2, 3); lp_build_endif(ctx); lp_build_flow_scope_end(flow); lp_build_flow_destroy(flow); */ /** * Begin an if/else/endif construct. */ void lp_build_if(struct lp_build_if_state *ctx, struct lp_build_flow_context *flow, LLVMBuilderRef builder, LLVMValueRef condition) { LLVMBasicBlockRef block = LLVMGetInsertBlock(builder); struct lp_build_flow_if *ifthen; unsigned i; memset(ctx, 0, sizeof(*ctx)); ctx->builder = builder; ctx->flow = flow; /* push/create new scope */ ifthen = &lp_build_flow_push(flow, LP_BUILD_FLOW_IF)->ifthen; assert(ifthen); ifthen->num_variables = flow->num_variables; ifthen->condition = condition; ifthen->entry_block = block; /* create a Phi node for each variable in this flow scope */ ifthen->phi = MALLOC(ifthen->num_variables * sizeof(*ifthen->phi)); if (!ifthen->phi) { ifthen->num_variables = 0; return; } /* create endif/merge basic block for the phi functions */ ifthen->merge_block = lp_build_insert_new_block(builder, "endif-block"); LLVMPositionBuilderAtEnd(builder, ifthen->merge_block); /* create a phi node for each variable */ for (i = 0; i < flow->num_variables; i++) ifthen->phi[i] = LLVMBuildPhi(builder, LLVMTypeOf(*flow->variables[i]), ""); /* create/insert true_block before merge_block */ ifthen->true_block = LLVMInsertBasicBlock(ifthen->merge_block, "if-true-block"); /* successive code goes into the true block */ LLVMPositionBuilderAtEnd(builder, ifthen->true_block); } /** * Begin else-part of a conditional */ void lp_build_else(struct lp_build_if_state *ctx) { struct lp_build_flow_context *flow = ctx->flow; struct lp_build_flow_if *ifthen; unsigned i; ifthen = &lp_build_flow_peek(flow, LP_BUILD_FLOW_IF)->ifthen; assert(ifthen); /* for each variable, update the Phi node with a (variable, block) pair */ LLVMPositionBuilderAtEnd(ctx->builder, ifthen->merge_block); for (i = 0; i < flow->num_variables; i++) { assert(*flow->variables[i]); LLVMAddIncoming(ifthen->phi[i], flow->variables[i], &ifthen->true_block, 1); } /* create/insert false_block before the merge block */ ifthen->false_block = LLVMInsertBasicBlock(ifthen->merge_block, "if-false-block"); /* successive code goes into the else block */ LLVMPositionBuilderAtEnd(ctx->builder, ifthen->false_block); } /** * End a conditional. */ void lp_build_endif(struct lp_build_if_state *ctx) { struct lp_build_flow_context *flow = ctx->flow; struct lp_build_flow_if *ifthen; unsigned i; ifthen = &lp_build_flow_pop(flow, LP_BUILD_FLOW_IF)->ifthen; assert(ifthen); if (ifthen->false_block) { LLVMPositionBuilderAtEnd(ctx->builder, ifthen->merge_block); /* for each variable, update the Phi node with a (variable, block) pair */ for (i = 0; i < flow->num_variables; i++) { assert(*flow->variables[i]); LLVMAddIncoming(ifthen->phi[i], flow->variables[i], &ifthen->false_block, 1); } } else { /* no else clause */ LLVMPositionBuilderAtEnd(ctx->builder, ifthen->merge_block); for (i = 0; i < flow->num_variables; i++) { LLVMValueRef undef; assert(*flow->variables[i]); LLVMAddIncoming(ifthen->phi[i], flow->variables[i], &ifthen->true_block, 1); /* undef value from the block preceeding the 'if' */ undef = LLVMGetUndef(LLVMTypeOf(*flow->variables[i])); LLVMAddIncoming(ifthen->phi[i], &undef, &ifthen->entry_block, 1); } } FREE(ifthen->phi); /*** *** Now patch in the various branch instructions. ***/ /* Insert the conditional branch instruction at the end of entry_block */ LLVMPositionBuilderAtEnd(ctx->builder, ifthen->entry_block); if (ifthen->false_block) { /* we have an else clause */ LLVMBuildCondBr(ctx->builder, ifthen->condition, ifthen->true_block, ifthen->false_block); } else { /* no else clause */ LLVMBuildCondBr(ctx->builder, ifthen->condition, ifthen->true_block, ifthen->merge_block); } /* Append an unconditional Br(anch) instruction on the true_block */ LLVMPositionBuilderAtEnd(ctx->builder, ifthen->true_block); LLVMBuildBr(ctx->builder, ifthen->merge_block); if (ifthen->false_block) { /* Append an unconditional Br(anch) instruction on the false_block */ LLVMPositionBuilderAtEnd(ctx->builder, ifthen->false_block); LLVMBuildBr(ctx->builder, ifthen->merge_block); } /* Resume building code at end of the ifthen->merge_block */ LLVMPositionBuilderAtEnd(ctx->builder, ifthen->merge_block); }