/**************************************************************************
|
*
|
* 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 <jfonseca@vmware.com>
|
*/
|
|
#include "util/u_debug.h"
|
#include "util/u_memory.h"
|
|
#include "lp_bld_init.h"
|
#include "lp_bld_type.h"
|
#include "lp_bld_flow.h"
|
|
|
/**
|
* Insert a new block, right where builder is pointing to.
|
*
|
* This is useful important not only for aesthetic reasons, but also for
|
* performance reasons, as frequently run blocks should be laid out next to
|
* each other and fall-throughs maximized.
|
*
|
* See also llvm/lib/Transforms/Scalar/BasicBlockPlacement.cpp.
|
*
|
* Note: this function has no dependencies on the flow code and could
|
* be used elsewhere.
|
*/
|
LLVMBasicBlockRef
|
lp_build_insert_new_block(struct gallivm_state *gallivm, const char *name)
|
{
|
LLVMBasicBlockRef current_block;
|
LLVMBasicBlockRef next_block;
|
LLVMBasicBlockRef new_block;
|
|
/* get current basic block */
|
current_block = LLVMGetInsertBlock(gallivm->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 = LLVMInsertBasicBlockInContext(gallivm->context, next_block, name);
|
}
|
else {
|
/* append new block after current block */
|
LLVMValueRef function = LLVMGetBasicBlockParent(current_block);
|
new_block = LLVMAppendBasicBlockInContext(gallivm->context, function, name);
|
}
|
|
return new_block;
|
}
|
|
|
/**
|
* 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_skip_context *skip,
|
struct gallivm_state *gallivm)
|
{
|
skip->gallivm = gallivm;
|
/* create new basic block */
|
skip->block = lp_build_insert_new_block(gallivm, "skip");
|
}
|
|
|
/**
|
* 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_skip_context *skip,
|
LLVMValueRef cond)
|
{
|
LLVMBasicBlockRef new_block;
|
|
new_block = lp_build_insert_new_block(skip->gallivm, "");
|
|
/* if cond is true, goto skip->block, else goto new_block */
|
LLVMBuildCondBr(skip->gallivm->builder, cond, skip->block, new_block);
|
|
LLVMPositionBuilderAtEnd(skip->gallivm->builder, new_block);
|
}
|
|
|
void
|
lp_build_flow_skip_end(struct lp_build_skip_context *skip)
|
{
|
/* goto block */
|
LLVMBuildBr(skip->gallivm->builder, skip->block);
|
LLVMPositionBuilderAtEnd(skip->gallivm->builder, skip->block);
|
}
|
|
|
/**
|
* Check if the mask predicate is zero. If so, jump to the end of the block.
|
*/
|
void
|
lp_build_mask_check(struct lp_build_mask_context *mask)
|
{
|
LLVMBuilderRef builder = mask->skip.gallivm->builder;
|
LLVMValueRef value;
|
LLVMValueRef cond;
|
|
value = lp_build_mask_value(mask);
|
|
/*
|
* XXX this doesn't quite generate the most efficient code possible, if
|
* the masks are vectors which have all bits set to the same value
|
* in each element.
|
* movmskps/pmovmskb would be more efficient to get the required value
|
* into ordinary reg (certainly with 8 floats).
|
* Not sure if llvm could figure that out on its own.
|
*/
|
|
/* cond = (mask == 0) */
|
cond = LLVMBuildICmp(builder,
|
LLVMIntEQ,
|
LLVMBuildBitCast(builder, value, mask->reg_type, ""),
|
LLVMConstNull(mask->reg_type),
|
"");
|
|
/* if cond, goto end of block */
|
lp_build_flow_skip_cond_break(&mask->skip, 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 gallivm_state *gallivm,
|
struct lp_type type,
|
LLVMValueRef value)
|
{
|
memset(mask, 0, sizeof *mask);
|
|
mask->reg_type = LLVMIntTypeInContext(gallivm->context, type.width * type.length);
|
mask->var = lp_build_alloca(gallivm,
|
lp_build_int_vec_type(gallivm, type),
|
"execution_mask");
|
|
LLVMBuildStore(gallivm->builder, value, mask->var);
|
|
lp_build_flow_skip_begin(&mask->skip, gallivm);
|
}
|
|
|
LLVMValueRef
|
lp_build_mask_value(struct lp_build_mask_context *mask)
|
{
|
return LLVMBuildLoad(mask->skip.gallivm->builder, mask->var, "");
|
}
|
|
|
/**
|
* 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_KILL_IF, etc.
|
*/
|
void
|
lp_build_mask_update(struct lp_build_mask_context *mask,
|
LLVMValueRef value)
|
{
|
value = LLVMBuildAnd(mask->skip.gallivm->builder,
|
lp_build_mask_value(mask),
|
value, "");
|
LLVMBuildStore(mask->skip.gallivm->builder, value, mask->var);
|
}
|
|
|
/**
|
* 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->skip);
|
return lp_build_mask_value(mask);
|
}
|
|
|
|
void
|
lp_build_loop_begin(struct lp_build_loop_state *state,
|
struct gallivm_state *gallivm,
|
LLVMValueRef start)
|
|
{
|
LLVMBuilderRef builder = gallivm->builder;
|
|
state->block = lp_build_insert_new_block(gallivm, "loop_begin");
|
|
state->counter_var = lp_build_alloca(gallivm, LLVMTypeOf(start), "loop_counter");
|
state->gallivm = gallivm;
|
|
LLVMBuildStore(builder, start, state->counter_var);
|
|
LLVMBuildBr(builder, state->block);
|
|
LLVMPositionBuilderAtEnd(builder, state->block);
|
|
state->counter = LLVMBuildLoad(builder, state->counter_var, "");
|
}
|
|
|
void
|
lp_build_loop_end_cond(struct lp_build_loop_state *state,
|
LLVMValueRef end,
|
LLVMValueRef step,
|
LLVMIntPredicate llvm_cond)
|
{
|
LLVMBuilderRef builder = state->gallivm->builder;
|
LLVMValueRef next;
|
LLVMValueRef cond;
|
LLVMBasicBlockRef after_block;
|
|
if (!step)
|
step = LLVMConstInt(LLVMTypeOf(end), 1, 0);
|
|
next = LLVMBuildAdd(builder, state->counter, step, "");
|
|
LLVMBuildStore(builder, next, state->counter_var);
|
|
cond = LLVMBuildICmp(builder, llvm_cond, next, end, "");
|
|
after_block = lp_build_insert_new_block(state->gallivm, "loop_end");
|
|
LLVMBuildCondBr(builder, cond, after_block, state->block);
|
|
LLVMPositionBuilderAtEnd(builder, after_block);
|
|
state->counter = LLVMBuildLoad(builder, state->counter_var, "");
|
}
|
|
|
void
|
lp_build_loop_end(struct lp_build_loop_state *state,
|
LLVMValueRef end,
|
LLVMValueRef step)
|
{
|
lp_build_loop_end_cond(state, end, step, LLVMIntNE);
|
}
|
|
/**
|
* Creates a c-style for loop,
|
* contrasts lp_build_loop as this checks condition on entry
|
* e.g. for(i = start; i cmp_op end; i += step)
|
* \param state the for loop state, initialized here
|
* \param gallivm the gallivm state
|
* \param start starting value of iterator
|
* \param cmp_op comparison operator used for comparing current value with end value
|
* \param end value used to compare against iterator
|
* \param step value added to iterator at end of each loop
|
*/
|
void
|
lp_build_for_loop_begin(struct lp_build_for_loop_state *state,
|
struct gallivm_state *gallivm,
|
LLVMValueRef start,
|
LLVMIntPredicate cmp_op,
|
LLVMValueRef end,
|
LLVMValueRef step)
|
{
|
LLVMBuilderRef builder = gallivm->builder;
|
|
assert(LLVMTypeOf(start) == LLVMTypeOf(end));
|
assert(LLVMTypeOf(start) == LLVMTypeOf(step));
|
|
state->begin = lp_build_insert_new_block(gallivm, "loop_begin");
|
state->step = step;
|
state->counter_var = lp_build_alloca(gallivm, LLVMTypeOf(start), "loop_counter");
|
state->gallivm = gallivm;
|
state->cond = cmp_op;
|
state->end = end;
|
|
LLVMBuildStore(builder, start, state->counter_var);
|
LLVMBuildBr(builder, state->begin);
|
|
LLVMPositionBuilderAtEnd(builder, state->begin);
|
state->counter = LLVMBuildLoad(builder, state->counter_var, "");
|
|
state->body = lp_build_insert_new_block(gallivm, "loop_body");
|
LLVMPositionBuilderAtEnd(builder, state->body);
|
}
|
|
/**
|
* End the for loop.
|
*/
|
void
|
lp_build_for_loop_end(struct lp_build_for_loop_state *state)
|
{
|
LLVMValueRef next, cond;
|
LLVMBuilderRef builder = state->gallivm->builder;
|
|
next = LLVMBuildAdd(builder, state->counter, state->step, "");
|
LLVMBuildStore(builder, next, state->counter_var);
|
LLVMBuildBr(builder, state->begin);
|
|
state->exit = lp_build_insert_new_block(state->gallivm, "loop_exit");
|
|
/*
|
* We build the comparison for the begin block here,
|
* if we build it earlier the output llvm ir is not human readable
|
* as the code produced is not in the standard begin -> body -> end order.
|
*/
|
LLVMPositionBuilderAtEnd(builder, state->begin);
|
cond = LLVMBuildICmp(builder, state->cond, state->counter, state->end, "");
|
LLVMBuildCondBr(builder, cond, state->body, state->exit);
|
|
LLVMPositionBuilderAtEnd(builder, state->exit);
|
}
|
|
|
/*
|
Example of if/then/else building:
|
|
int x;
|
if (cond) {
|
x = 1 + 2;
|
}
|
else {
|
x = 2 + 3;
|
}
|
|
Is built with:
|
|
// x needs an alloca variable
|
x = lp_build_alloca(builder, type, "x");
|
|
|
lp_build_if(ctx, builder, cond);
|
LLVMBuildStore(LLVMBuildAdd(1, 2), x);
|
lp_build_else(ctx);
|
LLVMBuildStore(LLVMBuildAdd(2, 3). x);
|
lp_build_endif(ctx);
|
|
*/
|
|
|
|
/**
|
* Begin an if/else/endif construct.
|
*/
|
void
|
lp_build_if(struct lp_build_if_state *ifthen,
|
struct gallivm_state *gallivm,
|
LLVMValueRef condition)
|
{
|
LLVMBasicBlockRef block = LLVMGetInsertBlock(gallivm->builder);
|
|
memset(ifthen, 0, sizeof *ifthen);
|
ifthen->gallivm = gallivm;
|
ifthen->condition = condition;
|
ifthen->entry_block = block;
|
|
/* create endif/merge basic block for the phi functions */
|
ifthen->merge_block = lp_build_insert_new_block(gallivm, "endif-block");
|
|
/* create/insert true_block before merge_block */
|
ifthen->true_block =
|
LLVMInsertBasicBlockInContext(gallivm->context,
|
ifthen->merge_block,
|
"if-true-block");
|
|
/* successive code goes into the true block */
|
LLVMPositionBuilderAtEnd(gallivm->builder, ifthen->true_block);
|
}
|
|
|
/**
|
* Begin else-part of a conditional
|
*/
|
void
|
lp_build_else(struct lp_build_if_state *ifthen)
|
{
|
LLVMBuilderRef builder = ifthen->gallivm->builder;
|
|
/* Append an unconditional Br(anch) instruction on the true_block */
|
LLVMBuildBr(builder, ifthen->merge_block);
|
|
/* create/insert false_block before the merge block */
|
ifthen->false_block =
|
LLVMInsertBasicBlockInContext(ifthen->gallivm->context,
|
ifthen->merge_block,
|
"if-false-block");
|
|
/* successive code goes into the else block */
|
LLVMPositionBuilderAtEnd(builder, ifthen->false_block);
|
}
|
|
|
/**
|
* End a conditional.
|
*/
|
void
|
lp_build_endif(struct lp_build_if_state *ifthen)
|
{
|
LLVMBuilderRef builder = ifthen->gallivm->builder;
|
|
/* Insert branch to the merge block from current block */
|
LLVMBuildBr(builder, ifthen->merge_block);
|
|
/*
|
* Now patch in the various branch instructions.
|
*/
|
|
/* Insert the conditional branch instruction at the end of entry_block */
|
LLVMPositionBuilderAtEnd(builder, ifthen->entry_block);
|
if (ifthen->false_block) {
|
/* we have an else clause */
|
LLVMBuildCondBr(builder, ifthen->condition,
|
ifthen->true_block, ifthen->false_block);
|
}
|
else {
|
/* no else clause */
|
LLVMBuildCondBr(builder, ifthen->condition,
|
ifthen->true_block, ifthen->merge_block);
|
}
|
|
/* Resume building code at end of the ifthen->merge_block */
|
LLVMPositionBuilderAtEnd(builder, ifthen->merge_block);
|
}
|
|
|
static LLVMBuilderRef
|
create_builder_at_entry(struct gallivm_state *gallivm)
|
{
|
LLVMBuilderRef builder = gallivm->builder;
|
LLVMBasicBlockRef current_block = LLVMGetInsertBlock(builder);
|
LLVMValueRef function = LLVMGetBasicBlockParent(current_block);
|
LLVMBasicBlockRef first_block = LLVMGetEntryBasicBlock(function);
|
LLVMValueRef first_instr = LLVMGetFirstInstruction(first_block);
|
LLVMBuilderRef first_builder = LLVMCreateBuilderInContext(gallivm->context);
|
|
if (first_instr) {
|
LLVMPositionBuilderBefore(first_builder, first_instr);
|
} else {
|
LLVMPositionBuilderAtEnd(first_builder, first_block);
|
}
|
|
return first_builder;
|
}
|
|
|
/**
|
* Allocate a scalar (or vector) variable.
|
*
|
* Although not strictly part of control flow, control flow has deep impact in
|
* how variables should be allocated.
|
*
|
* The mem2reg optimization pass is the recommended way to dealing with mutable
|
* variables, and SSA. It looks for allocas and if it can handle them, it
|
* promotes them, but only looks for alloca instructions in the entry block of
|
* the function. Being in the entry block guarantees that the alloca is only
|
* executed once, which makes analysis simpler.
|
*
|
* See also:
|
* - http://www.llvm.org/docs/tutorial/OCamlLangImpl7.html#memory
|
*/
|
LLVMValueRef
|
lp_build_alloca(struct gallivm_state *gallivm,
|
LLVMTypeRef type,
|
const char *name)
|
{
|
LLVMBuilderRef builder = gallivm->builder;
|
LLVMBuilderRef first_builder = create_builder_at_entry(gallivm);
|
LLVMValueRef res;
|
|
res = LLVMBuildAlloca(first_builder, type, name);
|
LLVMBuildStore(builder, LLVMConstNull(type), res);
|
|
LLVMDisposeBuilder(first_builder);
|
|
return res;
|
}
|
|
|
/**
|
* Like lp_build_alloca, but do not zero-initialize the variable.
|
*/
|
LLVMValueRef
|
lp_build_alloca_undef(struct gallivm_state *gallivm,
|
LLVMTypeRef type,
|
const char *name)
|
{
|
LLVMBuilderRef first_builder = create_builder_at_entry(gallivm);
|
LLVMValueRef res;
|
|
res = LLVMBuildAlloca(first_builder, type, name);
|
|
LLVMDisposeBuilder(first_builder);
|
|
return res;
|
}
|
|
|
/**
|
* Allocate an array of scalars/vectors.
|
*
|
* mem2reg pass is not capable of promoting structs or arrays to registers, but
|
* we still put it in the first block anyway as failure to put allocas in the
|
* first block may prevent the X86 backend from successfully align the stack as
|
* required.
|
*
|
* Also the scalarrepl pass is supposedly more powerful and can promote
|
* arrays in many cases.
|
*
|
* See also:
|
* - http://www.llvm.org/docs/tutorial/OCamlLangImpl7.html#memory
|
*/
|
LLVMValueRef
|
lp_build_array_alloca(struct gallivm_state *gallivm,
|
LLVMTypeRef type,
|
LLVMValueRef count,
|
const char *name)
|
{
|
LLVMBuilderRef first_builder = create_builder_at_entry(gallivm);
|
LLVMValueRef res;
|
|
res = LLVMBuildArrayAlloca(first_builder, type, count, name);
|
|
LLVMDisposeBuilder(first_builder);
|
|
return res;
|
}
|