Lookupswitch is implemented in the simmilar way tableswitch is.
Lookupswitch requires two basic blocks. The first one performs binary
search to find the branch target. It's ended by if statement which
checks if the result is NULL (target not found). In this case default
target is taken. Otherwise the second basic block is entered which
simply jumps to the found target.

Currently, binary search is implemented by a call to standard
bsearch() function. It can be optimized in the future by inlining the
binary search code for small lookupswitch tables.

Signed-off-by: Tomek Grabiec <tgrab...@gmail.com>
---
 arch/x86/insn-selector.brg     |   31 +++++++++++++
 include/jit/compilation-unit.h |    1 +
 include/jit/expression.h       |    9 +++-
 include/jit/statement.h        |   27 ++++++++++++
 include/vm/bytecodes.h         |   25 +++++++++++
 jit/bytecode-to-ir.c           |   11 -----
 jit/cfg-analyzer.c             |   22 ++++++++--
 jit/compilation-unit.c         |   13 ++++++
 jit/compiler.c                 |    2 +-
 jit/emit.c                     |   27 ++++++++++++
 jit/expression.c               |   16 +++++++
 jit/statement.c                |   48 ++++++++++++++++++++
 jit/subroutine.c               |   21 ++++++++-
 jit/switch-bc.c                |   93 +++++++++++++++++++++++++++++++++++++++-
 jit/tree-printer.c             |   42 +++++++++++++++++-
 regression/jvm/SwitchTest.java |   64 +++++++++++++++++++++++++++
 vm/bytecodes.c                 |   24 ++++++++++
 17 files changed, 454 insertions(+), 22 deletions(-)

diff --git a/arch/x86/insn-selector.brg b/arch/x86/insn-selector.brg
index a8f18b6..7462ecc 100644
--- a/arch/x86/insn-selector.brg
+++ b/arch/x86/insn-selector.brg
@@ -2433,6 +2433,37 @@ stmt:    STMT_TABLESWITCH(reg)
                                           base, state->left->reg1, scale));
 }
 
+stmt:  STMT_LOOKUPSWITCH_JUMP(reg)
+{
+       select_insn(s, tree, membase_insn(INSN_PUSH_MEMBASE, state->left->reg1,
+                               offsetof(struct lookupswitch_pair, target)));
+       select_insn(s, tree, insn(INSN_RET));
+}
+
+reg:   EXPR_LOOKUPSWITCH_BSEARCH(reg)
+{
+       struct expression *expr;
+
+       expr = to_expr(tree);
+
+       select_insn(s, tree, imm_insn(INSN_PUSH_IMM,
+                                     (unsigned long) &lookupswitch_pair_comp));
+
+       select_insn(s, tree, imm_insn(INSN_PUSH_IMM,
+                                     sizeof(struct lookupswitch_pair)));
+
+       select_insn(s, tree, imm_insn(INSN_PUSH_IMM,
+                                     expr->lookupswitch_table->count));
+
+       select_insn(s, tree, imm_insn(INSN_PUSH_IMM,
+                       (unsigned long) expr->lookupswitch_table->pairs));
+
+       select_insn(s, tree, reg_insn(INSN_PUSH_REG, state->left->reg1));
+
+       state->reg1 = get_fixed_var(s->b_parent, MACH_REG_EAX);
+       select_insn(s, tree, rel_insn(INSN_CALL_REL, (unsigned long) &bsearch));
+}
+
 stmt:  STMT_MONITOR_ENTER(reg)
 {
        struct var_info *ref;
diff --git a/include/jit/compilation-unit.h b/include/jit/compilation-unit.h
index 37178c1..b2e1d14 100644
--- a/include/jit/compilation-unit.h
+++ b/include/jit/compilation-unit.h
@@ -57,6 +57,7 @@ struct compilation_unit {
 
        struct list_head static_fixup_site_list;
        struct list_head tableswitch_list;
+       struct list_head lookupswitch_list;
 
        /*
         * This holds a pointer to the method's code. It's value is
diff --git a/include/jit/expression.h b/include/jit/expression.h
index e97a7c8..08f773c 100644
--- a/include/jit/expression.h
+++ b/include/jit/expression.h
@@ -12,6 +12,7 @@
 
 #include "arch/instruction.h"
 
+struct lookupswitch;
 struct parse_context;
 
 enum expression_type {
@@ -55,6 +56,7 @@ enum expression_type {
        EXPR_ARRAY_SIZE_CHECK,
        EXPR_MULTIARRAY_SIZE_CHECK,
        EXPR_MIMIC_STACK_SLOT,
+       EXPR_LOOKUPSWITCH_BSEARCH,
        EXPR_LAST,      /* Not a real type. Keep this last. */
 };
 
@@ -282,6 +284,11 @@ struct expression {
                        int slot_ndx;
                };
 
+               /* EXPR_LOOKUPSWITCH_BSEARCH */
+               struct  {
+                       struct tree_node *key;
+                       struct lookupswitch *lookupswitch_table;
+               };
        };
 };
 
@@ -357,7 +364,7 @@ struct expression *array_size_check_expr(struct expression 
*);
 struct expression *multiarray_size_check_expr(struct expression *);
 struct expression *dup_expr(struct parse_context *, struct expression *);
 struct expression *get_pure_expr(struct parse_context *, struct expression *);
-
+struct expression *lookupswitch_bsearch_expr(struct expression *, struct 
lookupswitch *);
 unsigned long nr_args(struct expression *);
 int expr_nr_kids(struct expression *);
 int expr_is_pure(struct expression *);
diff --git a/include/jit/statement.h b/include/jit/statement.h
index 8a4694c..612d8b2 100644
--- a/include/jit/statement.h
+++ b/include/jit/statement.h
@@ -7,6 +7,7 @@
 #include "vm/vm.h"
 
 struct tableswitch_info;
+struct lookupswitch_info;
 
 enum statement_type {
        STMT_STORE = OP_LAST,
@@ -22,6 +23,7 @@ enum statement_type {
        STMT_CHECKCAST,
        STMT_ARRAY_STORE_CHECK,
        STMT_TABLESWITCH,
+       STMT_LOOKUPSWITCH_JUMP,
        STMT_LAST,      /* Not a real type. Keep this last.  */
 };
 
@@ -42,6 +44,25 @@ struct tableswitch {
        struct list_head list_node;
 };
 
+struct lookupswitch_pair {
+       int32_t match;
+       union {
+               struct basic_block *bb_target;
+               void *target;
+       };
+} __attribute__((packed));
+
+struct lookupswitch {
+       uint32_t count;
+
+       /* basic block containing this lookupswitch. */
+       struct basic_block *src;
+
+       struct lookupswitch_pair *pairs;
+
+       struct list_head list_node;
+};
+
 struct statement {
        union {
                struct tree_node node;
@@ -77,6 +98,9 @@ struct statement {
                        struct tree_node *index;
                        struct tableswitch *table;
                };
+               struct /* STMT_LOOKUPSWITCH_JUMP */ {
+                       struct tree_node *lookupswitch_target;
+               };
 
                /* STMT_EXPRESSION, STMT_ARRAY_CHECK */
                struct tree_node *expression;
@@ -101,6 +125,9 @@ int stmt_nr_kids(struct statement *);
 
 struct tableswitch *alloc_tableswitch(struct tableswitch_info *, struct 
compilation_unit *, struct basic_block *, unsigned long);
 void free_tableswitch(struct tableswitch *);
+struct lookupswitch *alloc_lookupswitch(struct lookupswitch_info *, struct 
compilation_unit *, struct basic_block *, unsigned long);
+void free_lookupswitch(struct lookupswitch *);
+int lookupswitch_pair_comp(const void *, const void *);
 struct statement *if_stmt(struct basic_block *, enum vm_type, enum 
binary_operator, struct expression *, struct expression *);
 
 #define for_each_stmt(stmt, stmt_list) list_for_each_entry(stmt, stmt_list, 
stmt_list_node)
diff --git a/include/vm/bytecodes.h b/include/vm/bytecodes.h
index 836c19c..503def9 100644
--- a/include/vm/bytecodes.h
+++ b/include/vm/bytecodes.h
@@ -1,6 +1,8 @@
 #ifndef __JIT_BYTECODES_H
 #define __JIT_BYTECODES_H
 
+#include "vm/bytecode.h"
+
 #include <stdint.h>
 #include <stdbool.h>
 
@@ -40,9 +42,32 @@ struct tableswitch_info {
        unsigned long insn_size;
 };
 
+struct lookupswitch_info {
+       int32_t default_target;
+       const unsigned char *pairs;
+
+       uint32_t count;
+       unsigned long insn_size;
+};
+
 void get_tableswitch_info(const unsigned char *code, unsigned long pc,
                          struct tableswitch_info *info);
 
+void get_lookupswitch_info(const unsigned char *code, unsigned long pc,
+                          struct lookupswitch_info *info);
+
+static inline int32_t read_lookupswitch_target(struct lookupswitch_info *info,
+                                              unsigned int idx)
+{
+       return read_s32(info->pairs + idx * 8 + 4);
+}
+
+static inline int32_t read_lookupswitch_match(struct lookupswitch_info *info,
+                                             unsigned int idx)
+{
+       return read_s32(info->pairs + idx * 8);
+}
+
 static inline unsigned long bytecode_insn_count(const unsigned char *code,
                                                unsigned long code_length)
 {
diff --git a/jit/bytecode-to-ir.c b/jit/bytecode-to-ir.c
index 69107e5..c4c3f9b 100644
--- a/jit/bytecode-to-ir.c
+++ b/jit/bytecode-to-ir.c
@@ -44,17 +44,6 @@
 #include <errno.h>
 #include <stdio.h>
 
-/*
- * The following bytecodes are not supported yet.
- */
-
-static int convert_not_implemented(struct parse_context *ctx)
-{
-       return warn("bytecode %d is not supported", ctx->opc), -EINVAL;
-}
-
-#define convert_lookupswitch   convert_not_implemented
-
 #define convert_goto_w         convert_goto
 
 #define BYTECODE(opc, name, size, type) [opc] = convert_ ## name,
diff --git a/jit/cfg-analyzer.c b/jit/cfg-analyzer.c
index fe753ed..72a4ade 100644
--- a/jit/cfg-analyzer.c
+++ b/jit/cfg-analyzer.c
@@ -133,10 +133,10 @@ static int split_after_branches(const unsigned char *code,
                        get_tableswitch_info(code, offset, &info);
 
                        /*
-                        * We mark tableswitch targets to be split but
-                        * we do not connect them to this basic block
-                        * because it will be later split in
-                        * convert_tableswitch().
+                        * We mark tableswitch (and lookupswitch)
+                        * targets to be split but we do not connect
+                        * them to this basic block because it will be
+                        * later split in convert_tableswitch().
                         */
                        set_bit(branch_targets->bits,
                                offset + info.default_target);
@@ -147,6 +147,20 @@ static int split_after_branches(const unsigned char *code,
                                target = read_s32(info.targets + i * 4);
                                set_bit(branch_targets->bits, offset + target);
                        }
+               } else if (opcode == OPC_LOOKUPSWITCH) {
+                       struct lookupswitch_info info;
+
+                       get_lookupswitch_info(code, offset, &info);
+
+                       set_bit(branch_targets->bits,
+                               offset + info.default_target);
+
+                       for (unsigned int i = 0; i < info.count; i++) {
+                               int32_t target;
+
+                               target = read_lookupswitch_target(&info, i);
+                               set_bit(branch_targets->bits, offset + target);
+                       }
                } else if (bc_is_branch(opcode)) {
                        br_target_off = bc_target_off(&code[offset]) + offset;
 
diff --git a/jit/compilation-unit.c b/jit/compilation-unit.c
index f067e11..71eed13 100644
--- a/jit/compilation-unit.c
+++ b/jit/compilation-unit.c
@@ -96,6 +96,7 @@ struct compilation_unit *compilation_unit_alloc(struct 
vm_method *method)
 
                INIT_LIST_HEAD(&cu->static_fixup_site_list);
                INIT_LIST_HEAD(&cu->tableswitch_list);
+               INIT_LIST_HEAD(&cu->lookupswitch_list);
 
                for (unsigned int i = 0; i < NR_FIXED_REGISTERS; ++i) {
                        struct var_info *ret;
@@ -150,6 +151,17 @@ static void free_tableswitch_list(struct compilation_unit 
*cu)
        }
 }
 
+static void free_lookupswitch_list(struct compilation_unit *cu)
+{
+       struct lookupswitch *this, *next;
+
+       list_for_each_entry_safe(this, next, &cu->lookupswitch_list, list_node)
+       {
+               list_del(&this->list_node);
+               free_lookupswitch(this);
+       }
+}
+
 static void free_lir_insn_map(struct compilation_unit *cu)
 {
        free_radix_tree(cu->lir_insn_map);
@@ -170,6 +182,7 @@ void free_compilation_unit(struct compilation_unit *cu)
        free_stack_frame(cu->stack_frame);
        free_bc_offset_map(cu->bc_offset_map);
        free_tableswitch_list(cu);
+       free_lookupswitch_list(cu);
        free_lir_insn_map(cu);
        free(cu);
 }
diff --git a/jit/compiler.c b/jit/compiler.c
index 2f94e2f..f1dcee9 100644
--- a/jit/compiler.c
+++ b/jit/compiler.c
@@ -65,7 +65,7 @@ int compile(struct compilation_unit *cu)
        err = analyze_control_flow(cu);
        if (err)
                goto out;
-
+       trace_flush();
        err = convert_to_ir(cu);
        if (err)
                goto out;
diff --git a/jit/emit.c b/jit/emit.c
index b7bebad..1a45a1f 100644
--- a/jit/emit.c
+++ b/jit/emit.c
@@ -75,6 +75,22 @@ static void backpatch_tableswitch(struct tableswitch *table)
        }
 }
 
+static void backpatch_lookupswitch(struct lookupswitch *table)
+{
+       for (unsigned int i = 0; i < table->count; i++) {
+               int idx = bb_lookup_successor_index(table->src,
+                                                   table->pairs[i].bb_target);
+
+               if (branch_needs_resolution_block(table->src, idx)) {
+                       table->pairs[i].target =
+                               (void *)table->src->resolution_blocks[idx].addr;
+               } else {
+                       table->pairs[i].target =
+                               bb_native_ptr(table->pairs[i].bb_target);
+               }
+       }
+}
+
 static void backpatch_tableswitch_targets(struct compilation_unit *cu)
 {
        struct tableswitch *this;
@@ -85,6 +101,16 @@ static void backpatch_tableswitch_targets(struct 
compilation_unit *cu)
        }
 }
 
+static void backpatch_lookupswitch_targets(struct compilation_unit *cu)
+{
+       struct lookupswitch *this;
+
+       list_for_each_entry(this, &cu->lookupswitch_list, list_node)
+       {
+               backpatch_lookupswitch(this);
+       }
+}
+
 void emit_body(struct basic_block *bb, struct buffer *buf)
 {
        struct insn *insn;
@@ -176,6 +202,7 @@ int emit_machine_code(struct compilation_unit *cu)
        }
 
        backpatch_tableswitch_targets(cu);
+       backpatch_lookupswitch_targets(cu);
 
        jit_text_reserve(buffer_offset(cu->objcode));
        jit_text_unlock();
diff --git a/jit/expression.c b/jit/expression.c
index a2ddd97..7f04fa9 100644
--- a/jit/expression.c
+++ b/jit/expression.c
@@ -65,6 +65,7 @@ int expr_nr_kids(struct expression *expr)
        case EXPR_NULL_CHECK:
        case EXPR_ARRAY_SIZE_CHECK:
        case EXPR_MULTIARRAY_SIZE_CHECK:
+       case EXPR_LOOKUPSWITCH_BSEARCH:
                return 1;
        case EXPR_VALUE:
        case EXPR_FLOAT_LOCAL:
@@ -116,6 +117,7 @@ int expr_is_pure(struct expression *expr)
        case EXPR_ARRAY_SIZE_CHECK:
        case EXPR_MULTIARRAY_SIZE_CHECK:
        case EXPR_NULL_CHECK:
+       case EXPR_LOOKUPSWITCH_BSEARCH:
 
                /* These expression types should be always assumed to
                   have side-effects. */
@@ -614,3 +616,17 @@ struct expression *multiarray_size_check_expr(struct 
expression *dimensions)
 
        return expr;
 }
+
+struct expression *lookupswitch_bsearch_expr(struct expression *key, struct 
lookupswitch *table)
+{
+       struct expression *expr;
+
+       expr = alloc_expression(EXPR_LOOKUPSWITCH_BSEARCH, vm_pointer_type());
+       if (!expr)
+               return NULL;
+
+       expr->key = &key->node;
+       expr->lookupswitch_table = table;
+
+       return expr;
+}
diff --git a/jit/statement.c b/jit/statement.c
index 635e0e2..1010739 100644
--- a/jit/statement.c
+++ b/jit/statement.c
@@ -31,6 +31,7 @@ int stmt_nr_kids(struct statement *stmt)
        case STMT_MONITOR_EXIT:
        case STMT_CHECKCAST:
        case STMT_TABLESWITCH:
+       case STMT_LOOKUPSWITCH_JUMP:
                return 1;
        case STMT_GOTO:
        case STMT_VOID_RETURN:
@@ -106,3 +107,50 @@ void free_tableswitch(struct tableswitch *table)
        free(table->lookup_table);
        free(table);
 }
+
+struct lookupswitch *alloc_lookupswitch(struct lookupswitch_info *info,
+                                      struct compilation_unit *cu,
+                                      struct basic_block *bb,
+                                      unsigned long offset)
+{
+       struct lookupswitch *table;
+
+       table = malloc(sizeof(*table));
+       if (!table)
+               return NULL;
+
+       table->src = bb;
+
+       table->count = info->count;
+
+       table->pairs = malloc(sizeof(struct lookupswitch_pair) * info->count);
+       if (!table->pairs) {
+               free(table);
+               return NULL;
+       }
+
+       for (unsigned int i = 0; i < info->count; i++) {
+               int32_t target;
+
+               target = read_lookupswitch_target(info, i);
+               table->pairs[i].match = read_lookupswitch_match(info, i);
+               table->pairs[i].bb_target = find_bb(cu, offset + target);
+       }
+
+       list_add(&table->list_node, &cu->lookupswitch_list);
+
+       return table;
+}
+
+void free_lookupswitch(struct lookupswitch *table)
+{
+       free(table->pairs);
+       free(table);
+}
+
+int lookupswitch_pair_comp(const void *key, const void *elem)
+{
+       const struct lookupswitch_pair *pair = elem;
+
+       return (int32_t)key - pair->match;
+}
diff --git a/jit/subroutine.c b/jit/subroutine.c
index ad6f931..a52649a 100644
--- a/jit/subroutine.c
+++ b/jit/subroutine.c
@@ -375,8 +375,25 @@ static int do_subroutine_scan(struct 
subroutine_scan_context *ctx,
                        break;
                }
 
-               if (c[pc] == OPC_LOOKUPSWITCH)
-                       error("not implemented");
+               if (c[pc] == OPC_LOOKUPSWITCH) {
+                       struct lookupswitch_info info;
+
+                       get_lookupswitch_info(c, pc, &info);
+                       err = do_subroutine_scan(ctx, pc + info.default_target);
+                       if (err)
+                               return err;
+
+                       for (unsigned int i = 0; i < info.count; i++) {
+                               int32_t target;
+
+                               target = read_s32(info.pairs + i * 8 + 4);
+                               err = do_subroutine_scan(ctx, pc + target);
+                               if (err)
+                                       return err;
+                       }
+
+                       break;
+               }
 
                if (bc_is_jsr(c[pc])) {
                        unsigned long target = pc + bc_target_off(&c[pc]);
diff --git a/jit/switch-bc.c b/jit/switch-bc.c
index ce7cd44..97ea781 100644
--- a/jit/switch-bc.c
+++ b/jit/switch-bc.c
@@ -63,6 +63,18 @@ static struct statement *branch_if_greater_stmt(struct 
basic_block *target,
        return if_stmt(target, J_INT, OP_GT, left, right_expr);
 }
 
+static struct statement *branch_if_null_stmt(struct basic_block *target,
+                                            struct expression *left)
+{
+       struct expression *right_expr;
+
+       right_expr = value_expr(vm_pointer_type(), 0);
+       if (!right_expr)
+               return NULL;
+
+       return if_stmt(target, vm_pointer_type(), OP_EQ, left, right_expr);
+}
+
 int convert_tableswitch(struct parse_context *ctx)
 {
        struct tableswitch_info info;
@@ -84,11 +96,11 @@ int convert_tableswitch(struct parse_context *ctx)
        b1 = bb_split(master_bb, ctx->offset);
        b2 = bb_split(b1, ctx->offset);
 
+       assert(b1 && b2);
+
        b1->is_converted = true;
        b2->is_converted = true;
 
-       assert(b1 && b2);
-
        bb_add_successor(master_bb, default_bb );
        bb_add_successor(master_bb, b1);
 
@@ -147,3 +159,80 @@ int convert_tableswitch(struct parse_context *ctx)
  fail_default_bb:
        return -1;
 }
+
+int convert_lookupswitch(struct parse_context *ctx)
+{
+       struct lookupswitch_info info;
+       struct lookupswitch *table;
+       struct basic_block *master_bb;
+       struct basic_block *default_bb;
+       struct basic_block *b1;
+
+       get_lookupswitch_info(ctx->code, ctx->offset, &info);
+       ctx->buffer->pos += info.insn_size;
+
+       default_bb = find_bb(ctx->cu, ctx->offset + info.default_target);
+       if (!default_bb)
+               goto fail_default_bb;
+
+       master_bb = ctx->bb;
+
+       b1 = bb_split(master_bb, ctx->offset);
+
+       assert(b1);
+
+       b1->is_converted = true;
+
+       bb_add_successor(master_bb, default_bb );
+       bb_add_successor(master_bb, b1);
+
+       for (unsigned int i = 0; i < info.count; i++) {
+               struct basic_block *target_bb;
+               int32_t target;
+
+               target = read_lookupswitch_target(&info, i);
+               target_bb = find_bb(ctx->cu, ctx->offset + target);
+
+               bb_add_successor(b1, target_bb);
+       }
+
+       table = alloc_lookupswitch(&info, ctx->cu, b1, ctx->offset);
+       if (!table)
+               return -ENOMEM;
+
+       struct statement *if_null_stmt;
+       struct statement *stmt;
+       struct expression *key;
+       struct expression *bsearch;
+       struct expression *pure_bsearch;
+
+       key = stack_pop(ctx->bb->mimic_stack);
+
+       bsearch = lookupswitch_bsearch_expr(key, table);
+       if (!bsearch)
+               return -1;
+
+       pure_bsearch = get_pure_expr(ctx, bsearch);
+
+       if_null_stmt =
+               branch_if_null_stmt(default_bb, pure_bsearch);
+       if (!if_null_stmt)
+               goto fail_null_stmt;
+
+       stmt = alloc_statement(STMT_LOOKUPSWITCH_JUMP);
+       if (!stmt)
+               goto fail_stmt;
+
+       stmt->lookupswitch_target = &pure_bsearch->node;
+
+       convert_statement(ctx, if_null_stmt);
+       do_convert_statement(b1, stmt, ctx->offset);
+       return 0;
+
+ fail_stmt:
+       free_statement(if_null_stmt);
+ fail_null_stmt:
+       free_lookupswitch(table);
+ fail_default_bb:
+       return -1;
+}
diff --git a/jit/tree-printer.c b/jit/tree-printer.c
index e9ede43..b20d80a 100644
--- a/jit/tree-printer.c
+++ b/jit/tree-printer.c
@@ -322,6 +322,22 @@ out:
        return err;
 }
 
+static int print_lookupswitch_jump_stmt(int lvl, struct string *str,
+                                       struct statement *stmt)
+{
+       int err;
+
+       err = append_formatted(lvl, str, "LOOKUPSWITCH_JUMP:\n");
+       if (err)
+               goto out;
+
+       err = append_tree_attr(lvl + 1, str, "target",
+                              stmt->lookupswitch_target);
+
+ out:
+       return err;
+}
+
 typedef int (*print_stmt_fn) (int, struct string * str, struct statement *);
 
 static print_stmt_fn stmt_printers[] = {
@@ -338,6 +354,7 @@ static print_stmt_fn stmt_printers[] = {
        [STMT_ATHROW] = print_athrow_stmt,
        [STMT_ARRAY_STORE_CHECK] = print_array_store_check_stmt,
        [STMT_TABLESWITCH] = print_tableswitch_stmt,
+       [STMT_LOOKUPSWITCH_JUMP] = print_lookupswitch_jump_stmt,
 };
 
 static int print_stmt(int lvl, struct tree_node *root, struct string *str)
@@ -1001,6 +1018,29 @@ out:
        return err;
 }
 
+static int print_lookupswitch_bsearch_expr(int lvl, struct string *str,
+                                          struct expression *expr)
+{
+       int err;
+
+       err = append_formatted(lvl, str, "LOOKUPSWITCH_BSEARCH:\n");
+       if (err)
+               goto out;
+
+       struct lookupswitch *table = expr->lookupswitch_table;
+
+       for (unsigned int i = 0; i < table->count; i++) {
+               char case_name[16];
+
+               sprintf(case_name, "case %d", table->pairs[i].match);
+               err = append_simple_attr(lvl + 1, str, case_name, "bb %p",
+                                        table->pairs[i].bb_target);
+       }
+
+out:
+       return err;
+}
+
 typedef int (*print_expr_fn) (int, struct string * str, struct expression *);
 
 static print_expr_fn expr_printers[] = {
@@ -1044,7 +1084,7 @@ static print_expr_fn expr_printers[] = {
        [EXPR_ARRAY_SIZE_CHECK] = print_array_size_check_expr,
        [EXPR_MULTIARRAY_SIZE_CHECK] = print_multiarray_size_check_expr,
        [EXPR_MIMIC_STACK_SLOT] = print_mimic_stack_slot_expr,
-
+       [EXPR_LOOKUPSWITCH_BSEARCH] = print_lookupswitch_bsearch_expr,
 };
 
 static int print_expr(int lvl, struct tree_node *root, struct string *str)
diff --git a/regression/jvm/SwitchTest.java b/regression/jvm/SwitchTest.java
index 3726126..1e064ac 100644
--- a/regression/jvm/SwitchTest.java
+++ b/regression/jvm/SwitchTest.java
@@ -71,8 +71,72 @@ class SwitchTest extends TestCase {
         assertEquals(-4, index);
     }
 
+    public static void testLookupswitchCaseMatches() {
+        int index;
+
+        index = 4000;
+
+        switch (index) {
+        case -100:
+            index = -1;
+            break;
+        case 2000:
+            index = -2;
+            break;
+        case 4000:
+            index = -3;
+            break;
+        case 50000:
+            index = -4;
+            break;
+        case 221:
+            index = -5;
+            break;
+        case -34:
+            index = -6;
+            break;
+        default:
+            index = -7;
+        }
+
+        assertEquals(-3, index);
+    }
+
+    public static void testLookupswitchDefault() {
+        int index;
+
+        index = 314;
+
+        switch (index) {
+        case -100:
+            index = -1;
+            break;
+        case 2000:
+            index = -2;
+            break;
+        case 4000:
+            index = -3;
+            break;
+        case 50000:
+            index = -4;
+            break;
+        case 221:
+            index = -5;
+            break;
+        case -34:
+            index = -6;
+            break;
+        default:
+            index = -7;
+        }
+
+        assertEquals(-7, index);
+    }
+
     public static void main(String []args) {
         testSwitchCaseMatches();
         testSwitchDefault();
+        testLookupswitchCaseMatches();
+        testLookupswitchDefault();
     }
 }
\ No newline at end of file
diff --git a/vm/bytecodes.c b/vm/bytecodes.c
index 4f20421..1316c73 100644
--- a/vm/bytecodes.c
+++ b/vm/bytecodes.c
@@ -66,6 +66,23 @@ void get_tableswitch_info(const unsigned char *code, 
unsigned long pc,
        info->insn_size = 1 + pad + (3 + info->count) * 4;
 }
 
+void get_lookupswitch_info(const unsigned char *code, unsigned long pc,
+                          struct lookupswitch_info *info)
+{
+       unsigned long pad;
+
+       assert(code[pc] == OPC_LOOKUPSWITCH);
+
+       pad = 3 - (pc % 4);
+       pc += pad + 1;
+
+       info->default_target = read_u32(&code[pc]);
+       info->count = read_u32(&code[pc + 4]);
+       info->pairs = &code[pc + 8];
+
+       info->insn_size = 1 + pad + 2 * 4 + info->count * 8;
+}
+
 unsigned long bc_insn_size(const unsigned char *code, unsigned long pc)
 {
        unsigned long size;
@@ -84,6 +101,13 @@ unsigned long bc_insn_size(const unsigned char *code, 
unsigned long pc)
                return info.insn_size;
        }
 
+       if (code[pc] == OPC_LOOKUPSWITCH) {
+               struct lookupswitch_info info;
+
+               get_lookupswitch_info(code, pc, &info);
+               return info.insn_size;
+       }
+
        size = bytecode_infos[code[pc]].size;
 
        if (size == 0)
-- 
1.6.0.6


------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day 
trial. Simplify your report design, integration and deployment - and focus on 
what you do best, core application coding. Discover what's new with 
Crystal Reports now.  http://p.sf.net/sfu/bobj-july
_______________________________________________
Jatovm-devel mailing list
Jatovm-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/jatovm-devel

Reply via email to