llvmpipe: checkpoint commit of new if/else/endif flow control

Totally untested at this point.  More work to do.
This commit is contained in:
Brian Paul 2010-01-06 17:53:12 -07:00
parent db7f9b053b
commit baeb3a2351
2 changed files with 269 additions and 1 deletions

View file

@ -41,6 +41,8 @@
#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.
@ -48,6 +50,7 @@
enum lp_build_flow_construct_kind {
LP_BUILD_FLOW_SCOPE,
LP_BUILD_FLOW_SKIP,
LP_BUILD_FLOW_IF
};
@ -73,7 +76,24 @@ struct lp_build_flow_skip
/** Number of variables declared at the beginning */
unsigned num_variables;
LLVMValueRef *phi;
LLVMValueRef *phi; /**< array [num_variables] */
};
/**
* if/else/endif.
*/
struct lp_build_flow_if
{
unsigned num_variables;
/** phi variables in the true clause */
LLVMValueRef true_variables[LP_BUILD_IF_MAX_VARIABLES];
unsigned num_true_variables;
/** phi variables in the false clause */
LLVMValueRef false_variables[LP_BUILD_IF_MAX_VARIABLES];
unsigned num_false_variables;
};
@ -84,6 +104,7 @@ union lp_build_flow_construct_data
{
struct lp_build_flow_scope scope;
struct lp_build_flow_skip skip;
struct lp_build_flow_if ifthen;
};
@ -540,3 +561,223 @@ lp_build_loop_end(LLVMBuilderRef builder,
LLVMPositionBuilderAtEnd(builder, after_block);
}
/*
Example of if/then/else building:
int x;
if (cond) {
x = 1 + 2;
}
else {
x = 2 + 3;
}
Is built with:
flow = lp_build_flow_create(builder);
...
lp_build_flow_scope_declare(flow, "x");
lp_build_if(ctx, flow, builder, cond);
x = LLVMAdd(1, 2);
lp_build_if_phi_var(ctx, "x");
lp_build_else(ctx);
x = LLVMAdd(2, 3);
lp_build_if_phi_var(ctx, "x");
lp_build_endif(ctx);
...
flow = lp_build_flow_end(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);
LLVMValueRef function = LLVMGetBasicBlockParent(block);
struct lp_build_flow_if *ifthen;
memset(ctx, 0, sizeof(*ctx));
ctx->builder = builder;
ctx->flow = flow;
ctx->condition = condition;
ctx->entry_block = block;
/* push/create new scope */
ifthen = &lp_build_flow_push(flow, LP_BUILD_FLOW_IF)->ifthen;
assert(ifthen);
ifthen->num_variables = flow->num_variables;
ifthen->num_true_variables = 0;
ifthen->num_false_variables = 0;
/* allocate the block for the if/true clause */
ctx->true_block = LLVMAppendBasicBlock(function, "true block");
/* XXX is this correct ??? */
LLVMPositionBuilderAtEnd(builder, ctx->true_block);
}
/**
* Begin else-part of a conditional
*/
void
lp_build_else(struct lp_build_if_state *ctx)
{
LLVMBasicBlockRef block = LLVMGetInsertBlock(ctx->builder);
LLVMValueRef function = LLVMGetBasicBlockParent(block);
struct lp_build_flow_if *ifthen;
ifthen = &lp_build_flow_peek(ctx->flow, LP_BUILD_FLOW_IF)->ifthen;
assert(ifthen);
/* allocate the block for the else/false clause */
ctx->false_block = LLVMAppendBasicBlock(function, "false block");
/* XXX is this correct ??? */
LLVMPositionBuilderAtEnd(ctx->builder, ctx->false_block);
}
/**
* End a conditional.
* This involves building a "merge" block at the endif which
* contains the phi instructions.
*/
void
lp_build_endif(struct lp_build_if_state *ctx)
{
LLVMBasicBlockRef block = LLVMGetInsertBlock(ctx->builder);
LLVMValueRef function = LLVMGetBasicBlockParent(block);
LLVMBasicBlockRef merge_block = LLVMAppendBasicBlock(function, "endif block");
LLVMValueRef phi[LP_BUILD_FLOW_MAX_VARIABLES];
struct lp_build_flow_if *ifthen;
unsigned i;
/* build the endif/merge block now */
/* XXX this is probably wrong */
LLVMPositionBuilderAtEnd(ctx->builder, merge_block);
ifthen = &lp_build_flow_pop(ctx->flow, LP_BUILD_FLOW_IF)->ifthen;
assert(ifthen);
memset(phi, 0, sizeof(phi));
/* build phi nodes for any variables which were declared inside if part */
for (i = 0; i < ifthen->num_variables; i++) {
LLVMValueRef *var = ctx->flow->variables[i];
const char *name = LLVMGetValueName(*var);
unsigned j;
/* search true-clause variables list for 'name' */
for (j = 0; j < ifthen->num_true_variables; j++) {
LLVMValueRef v = ifthen->true_variables[j];
if (strcmp(LLVMGetValueName(v), name) == 0) {
/* add phi */
if (!phi[i])
phi[i] = LLVMBuildPhi(ctx->builder, LLVMTypeOf(*var), "");
LLVMAddIncoming(phi[i], &v, &ctx->true_block, 1);
}
}
/* search false-clause variables list for 'name' */
for (j = 0; j < ifthen->num_false_variables; j++) {
LLVMValueRef v = ifthen->false_variables[j];
if (strcmp(LLVMGetValueName(v), name) == 0) {
/* add phi */
if (!phi[i])
phi[i] = LLVMBuildPhi(ctx->builder, LLVMTypeOf(*var), "");
LLVMAddIncoming(phi[i], &v, &ctx->false_block, 1);
}
}
/* "return" new phi variable to calling code */
if (phi[i])
*var = phi[i];
}
/***
*** Insert the various branch instructions here.
*** XXX need to verify all the builder/block positioning is correct.
***/
/* Insert the conditional branch instruction at the end of entry_block */
LLVMPositionBuilderAtEnd(ctx->builder, ctx->entry_block);
if (ctx->false_block) {
/* we have an else clause */
LLVMBuildCondBr(ctx->builder, ctx->condition,
ctx->true_block, ctx->false_block);
}
else {
/* no else clause */
LLVMBuildCondBr(ctx->builder, ctx->condition,
ctx->true_block, merge_block);
}
/* Append an unconditional Br(anch) instruction on the true_block */
LLVMPositionBuilderAtEnd(ctx->builder, ctx->true_block);
LLVMBuildBr(ctx->builder, merge_block);
if (ctx->false_block) {
/* Append an unconditional Br(anch) instruction on the false_block */
LLVMPositionBuilderAtEnd(ctx->builder, ctx->false_block);
LLVMBuildBr(ctx->builder, merge_block);
}
/* Finish-up: continue building at end of the merge_block */
/* XXX is this right? */
LLVMPositionBuilderAtEnd(ctx->builder, merge_block);
}
/**
* Declare a variable that needs to be merged with another variable
* via a phi function.
* This function must be called after lp_build_if() and lp_build_endif().
*/
void
lp_build_if_phi_var(struct lp_build_if_state *ctx, LLVMValueRef var)
{
struct lp_build_flow_if *ifthen;
const char *name;
name = LLVMGetValueName(var);
assert(name && "variable requires a name");
/* make sure the var existed before the if/then/else */
{
boolean found = FALSE;
uint i;
for (i = 0; i < ctx->flow->num_variables; i++) {
LLVMValueRef *var = ctx->flow->variables[i];
if (strcmp(LLVMGetValueName(*var), name) == 0) {
found = TRUE;
break;
}
}
assert(found);
}
ifthen = &lp_build_flow_pop(ctx->flow, LP_BUILD_FLOW_IF)->ifthen;
if (ctx->false_block) {
ifthen->false_variables[ifthen->num_false_variables++] = var;
}
else {
assert(ctx->true_block);
ifthen->true_variables[ifthen->num_true_variables++] = var;
}
}

View file

@ -126,4 +126,31 @@ lp_build_loop_end(LLVMBuilderRef builder,
struct lp_build_if_state
{
LLVMBuilderRef builder;
struct lp_build_flow_context *flow;
LLVMValueRef condition;
LLVMBasicBlockRef entry_block, true_block, false_block;
};
void
lp_build_if(struct lp_build_if_state *ctx,
struct lp_build_flow_context *flow,
LLVMBuilderRef builder,
LLVMValueRef condition);
void
lp_build_if_phi_var(struct lp_build_if_state *ctx, LLVMValueRef var);
void
lp_build_else(struct lp_build_if_state *ctx);
void
lp_build_endif(struct lp_build_if_state *ctx);
#endif /* !LP_BLD_FLOW_H */