Skip to content

Commit

Permalink
Code generation for case statements with non-scalar values
Browse files Browse the repository at this point in the history
  • Loading branch information
nickg committed Apr 8, 2012
1 parent 8a3c76b commit 0f9c585
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 1 deletion.
63 changes: 62 additions & 1 deletion src/cgen.c
Expand Up @@ -1971,8 +1971,10 @@ static void cgen_exit(tree_t t, struct cgen_ctx *ctx)
LLVMPositionBuilderAtEnd(builder, not_bb);
}

static void cgen_case(tree_t t, struct cgen_ctx *ctx)
static void cgen_case_scalar(tree_t t, struct cgen_ctx *ctx)
{
// Case with scalar value maps onto LLVM case

LLVMBasicBlockRef exit_bb = LLVMAppendBasicBlock(ctx->fn, "case_exit");

LLVMBasicBlockRef else_bb = exit_bb;
Expand Down Expand Up @@ -2014,6 +2016,65 @@ static void cgen_case(tree_t t, struct cgen_ctx *ctx)
LLVMPositionBuilderAtEnd(builder, exit_bb);
}

static void cgen_case_array(tree_t t, struct cgen_ctx *ctx)
{
// Case with array value must use chain of ifs

// TODO: multiple calls to cgen_array_rel is very inefficient
// replace this with code to compare all values in a single
// loop (e.g. build a bit mask of length #assocs)

LLVMBasicBlockRef exit_bb = LLVMAppendBasicBlock(ctx->fn, "case_exit");

LLVMValueRef val = cgen_expr(tree_value(t), ctx);
type_t type = tree_type(tree_value(t));

bool have_others = false;
for (unsigned i = 0; i < tree_assocs(t); i++) {
LLVMBasicBlockRef next_bb = NULL;
assoc_t a = tree_assoc(t, i);
switch (a.kind) {
case A_NAMED:
{
LLVMBasicBlockRef this_bb =
LLVMAppendBasicBlock(ctx->fn, "case_body");
next_bb = LLVMAppendBasicBlock(ctx->fn, "case_test");
LLVMValueRef eq = cgen_array_rel(val, cgen_expr(a.name, ctx),
type, type, LLVMIntEQ, ctx);
LLVMBuildCondBr(builder, eq, this_bb, next_bb);
LLVMPositionBuilderAtEnd(builder, this_bb);
}
break;

case A_OTHERS:
next_bb = exit_bb;
have_others = true;
break;

default:
assert(false);
}

cgen_stmt(a.value, ctx);
LLVMBuildBr(builder, exit_bb);

LLVMPositionBuilderAtEnd(builder, next_bb);
}

if (!have_others)
LLVMBuildBr(builder, exit_bb);

LLVMPositionBuilderAtEnd(builder, exit_bb);
}

static void cgen_case(tree_t t, struct cgen_ctx *ctx)
{
if (type_is_array(tree_type(tree_value(t))))
cgen_case_array(t, ctx);
else
cgen_case_scalar(t, ctx);
}

static void cgen_pcall(tree_t t, struct cgen_ctx *ctx)
{
tree_t decl = tree_ref(t);
Expand Down
58 changes: 58 additions & 0 deletions test/regress/case2.vhd
@@ -0,0 +1,58 @@
entity case2 is
end entity;

architecture test of case2 is

function toint4(b : bit_vector(3 downto 0)) return integer is
begin
case b is
when X"0" => return 0;
when X"1" => return 1;
when X"2" => return 2;
when X"3" => return 3;
when X"4" => return 4;
when X"5" => return 5;
when X"6" => return 6;
when X"7" => return 7;
when X"8" => return 8;
when X"9" => return 9;
when X"a" => return 10;
when X"b" => return 11;
when X"c" => return 12;
when X"d" => return 13;
when X"e" => return 14;
when X"f" => return 15;
end case;
end function;

function toint3(b : bit_vector(3 downto 0)) return integer is
begin
case b is
when X"0" => return 0;
when X"1" => return 1;
when X"2" => return 2;
when X"3" => return 3;
when X"4" => return 4;
when X"5" => return 5;
when X"6" => return 6;
when X"7" => return 7;
when others => return -1;
end case;
end function;

begin

process is
variable b : bit_vector(3 downto 0);
begin
assert toint4(X"4") = 4;
b := X"a";
assert toint4(b) = 10;
assert toint4(X"f") = 15;
assert toint3(X"5") = 5;
assert toint3(X"c") = -1;
wait;
end process;

end architecture;

1 change: 1 addition & 0 deletions test/regress/testlist.txt
Expand Up @@ -63,3 +63,4 @@ synopsys1 normal
block1 normal
elab3 normal,gold
func6 normal
case2 normal

0 comments on commit 0f9c585

Please sign in to comment.