In accordance with the JVM specification, we should only initialize
classes when the putstatic/getstatic/invokestatic instructions are
_executed_ (there are a few other cases too, but the main point is
that we cannot do this at method compilation time).

In order to maintain execution speed, we use only a single instruction
for getstatic/putstatic. Initially, this instruction will access a
special "guard" page which is not mapped -- and therefore it will
trigger a segmentation fault. In the signal handler, we initialize the
class and go through the "fixup list", a list of references to all the
static fields for the class, and patch all the instructions so that
they point to the right address for that field's value. When the
signal handler returns, the instruction is restarted and this time
succeeds.

Signed-off-by: Vegard Nossum <vegard.nos...@gmail.com>
---
 Makefile                       |    1 +
 arch/x86/emit-code.c           |   43 +++++++++++++++++++++++++
 arch/x86/insn-selector_32.brg  |   69 +++++++++++++++++++++++++++++++---------
 include/jit/compilation-unit.h |    3 ++
 include/vm/class.h             |    9 +++++
 include/vm/field.h             |    5 ++-
 include/vm/static.h            |   36 +++++++++++++++++++++
 jit/compilation-unit.c         |    2 +
 jit/invoke-bc.c                |    5 ---
 jit/object-bc.c                |   12 -------
 jit/trampoline.c               |    6 +++-
 test/arch-x86/Makefile         |    1 +
 vm/class.c                     |   20 ++++++++++--
 vm/field.c                     |    6 ++-
 vm/jato.c                      |    3 ++
 vm/signal.c                    |    6 +++
 vm/static.c                    |   63 ++++++++++++++++++++++++++++++++++++
 17 files changed, 250 insertions(+), 40 deletions(-)
 create mode 100644 include/vm/static.h
 create mode 100644 vm/static.c

diff --git a/Makefile b/Makefile
index cced880..db69bb3 100644
--- a/Makefile
+++ b/Makefile
@@ -103,6 +103,7 @@ VM_OBJS = \
        vm/signal.o             \
        vm/stack.o              \
        vm/stack-trace.o        \
+       vm/static.o             \
        vm/types.o              \
        vm/utf8.o               \
        vm/zalloc.o
diff --git a/arch/x86/emit-code.c b/arch/x86/emit-code.c
index 88c24bc..2c55535 100644
--- a/arch/x86/emit-code.c
+++ b/arch/x86/emit-code.c
@@ -1092,6 +1092,49 @@ struct emitter emitters[] = {
        DECL_EMITTER(INSN_XOR_REG_REG, emit_xor_reg_reg, TWO_OPERANDS),
 };
 
+void fixup_static(struct vm_class *vmc)
+{
+       struct static_fixup_site *this, *next;
+
+       if (vm_class_ensure_init(vmc))
+               die("class wouldn't initialize.. oops\n");
+
+       list_for_each_entry_safe(this, next,
+               &vmc->static_fixup_site_list, vmc_node)
+       {
+               struct vm_field *vmf = this->vmf;
+               void *site_addr = buffer_ptr(this->cu->objcode)
+                       + this->insn->mach_offset;
+               void *new_target = vmc->static_values + vmf->offset;
+
+               cpu_write_u32(site_addr + 2, (unsigned long) new_target);
+
+               list_del(&this->vmc_node);
+               list_del(&this->cu_node);
+               free(this);
+       }
+}
+
+void fixup_static_at(unsigned long addr)
+{
+       struct compilation_unit *cu;
+       struct static_fixup_site *this;
+
+       cu = get_cu_from_native_addr(addr);
+       assert(cu);
+
+       list_for_each_entry(this, &cu->static_fixup_site_list, cu_node)
+       {
+               void *site_addr = buffer_ptr(cu->objcode)
+                       + this->insn->mach_offset;
+
+               if ((unsigned long) site_addr == addr) {
+                       fixup_static(this->vmf->class);
+                       return;
+               }
+       }
+}
+
 /*
  * This fixes relative calls generated by EXPR_INVOKE.
  *
diff --git a/arch/x86/insn-selector_32.brg b/arch/x86/insn-selector_32.brg
index 7c380d9..f375613 100644
--- a/arch/x86/insn-selector_32.brg
+++ b/arch/x86/insn-selector_32.brg
@@ -811,22 +811,42 @@ reg:      OP_LE(reg, reg) 2
 reg:   EXPR_CLASS_FIELD 1
 {
        struct expression *expr;
-       struct var_info *field;
-       long offset;
+       struct var_info *out;
+       struct insn *mov_insn;
+
+       struct vm_field *vmf; 
+       struct vm_class *vmc;
 
        expr   = to_expr(tree);
-       offset = offsetof(struct vm_field, static_value);
 
-       state->reg1 = get_var(s->b_parent);
-       field = get_var(s->b_parent);
+       out = get_var(s->b_parent);
+       state->reg1 = out;
+
+       vmf = expr->class_field;
+       vmc = vmf->class;
+
+       /* XXX: Lock class */
+       if (vmc->state >= VM_CLASS_INITIALIZING || vm_field_is_final(vmf)) {
+               /* Class is already initialized; no need for fix-up. We also
+                * don't want the fixup if we're already inside the
+                * initializer. */
+               mov_insn = memdisp_reg_insn(INSN_MOV_MEMDISP_REG,
+                       (unsigned long) vmc->static_values + vmf->offset, out);
+       } else {
+               mov_insn = memdisp_reg_insn(INSN_MOV_MEMDISP_REG,
+                       (unsigned long) static_guard_page, out);
+
+               /* XXX: Check return value */
+               add_getstatic_fixup_site(mov_insn, vmf, s->b_parent);
+       }
 
-       select_insn(s, tree, imm_reg_insn(INSN_MOV_IMM_REG, (unsigned long) 
expr->class_field, field));
-       select_insn(s, tree, membase_reg_insn(INSN_MOV_MEMBASE_REG, field, 
offset, state->reg1));
+       select_insn(s, tree, mov_insn);
 
        if (expr->vm_type == J_LONG) {
                state->reg2 = get_var(s->b_parent);
-
-               select_insn(s, tree, membase_reg_insn(INSN_MOV_MEMBASE_REG, 
field, offset+4, state->reg2));
+               select_insn(s, tree, memdisp_reg_insn(INSN_MOV_MEMDISP_REG,
+                       (unsigned long) vmc->static_values + vmf->offset + 4,
+                       state->reg2));
        }
 }
 
@@ -1159,23 +1179,42 @@ stmt:   STMT_STORE(EXPR_CLASS_FIELD, reg)
        struct var_info *offset;
        struct statement *stmt;
        struct var_info *src;
+       struct insn *mov_insn;
+
+       struct vm_field *vmf; 
+       struct vm_class *vmc;
 
        stmt = to_stmt(tree);
        store_dest = to_expr(stmt->store_dest);
        store_src  = to_expr(stmt->store_src);
 
-       field_offset = offsetof(struct vm_field, static_value);
        src = state->right->reg1;
 
-       offset = get_var(s->b_parent);
+       vmf = store_dest->class_field;
+       vmc = vmf->class;
+
+       /* XXX: Lock class */
+       if (vmc->state >= VM_CLASS_INITIALIZING || vm_field_is_final(vmf)) {
+               /* Class is already initialized; no need for fix-up. We also
+                * don't want the fixup if we're already inside the
+                * initializer. */
+               mov_insn = reg_memdisp_insn(INSN_MOV_REG_MEMDISP,
+                       src, (unsigned long) vmc->static_values + vmf->offset);
+       } else {
+               mov_insn = reg_memdisp_insn(INSN_MOV_REG_MEMDISP,
+                       src, (unsigned long) static_guard_page);
+
+               /* XXX: Check return value */
+               add_putstatic_fixup_site(mov_insn, vmf, s->b_parent);
+       }
 
-       select_insn(s, tree, imm_reg_insn(INSN_MOV_IMM_REG, (unsigned long) 
store_dest->class_field, offset));
-       select_insn(s, tree, reg_membase_insn(INSN_MOV_REG_MEMBASE, src, 
offset, field_offset));
+       select_insn(s, tree, mov_insn);
 
        if (store_src->vm_type == J_LONG) {
                src = state->right->reg2;
-               select_insn(s, tree, imm_reg_insn(INSN_ADD_IMM_REG, 4, offset));
-               select_insn(s, tree, reg_membase_insn(INSN_MOV_REG_MEMBASE, 
src, offset, field_offset));
+               select_insn(s, tree, reg_memdisp_insn(INSN_MOV_REG_MEMDISP,
+                       src,
+                       (unsigned long) vmc->static_values + vmf->offset + 4));
        }
 }
 
diff --git a/include/jit/compilation-unit.h b/include/jit/compilation-unit.h
index b960112..80dc880 100644
--- a/include/jit/compilation-unit.h
+++ b/include/jit/compilation-unit.h
@@ -5,6 +5,7 @@
 
 #include <vm/list.h>
 #include <vm/stack.h>
+#include <vm/static.h>
 
 #include <arch/stack-frame.h>
 
@@ -49,6 +50,8 @@ struct compilation_unit {
         */
        unsigned char *exit_past_unlock_ptr;
        unsigned char *unwind_past_unlock_ptr;
+
+       struct list_head static_fixup_site_list;
 };
 
 struct compilation_unit *compilation_unit_alloc(struct vm_method *);
diff --git a/include/vm/class.h b/include/vm/class.h
index a03db0e..8c27366 100644
--- a/include/vm/class.h
+++ b/include/vm/class.h
@@ -3,6 +3,7 @@
 
 #include <vm/field.h>
 #include <vm/method.h>
+#include <vm/static.h>
 #include <vm/types.h>
 #include <vm/vm.h>
 
@@ -13,6 +14,7 @@ struct vm_object;
 enum vm_class_state {
        VM_CLASS_LOADED,
        VM_CLASS_LINKED,
+       VM_CLASS_INITIALIZING,
        VM_CLASS_INITIALIZED,
 };
 
@@ -28,12 +30,19 @@ struct vm_class {
        struct vm_method *methods;
 
        unsigned int object_size;
+       unsigned int static_size;
 
        unsigned int vtable_size;
        struct vtable vtable;
 
        /* The java.lang.Class object representing this class */
        struct vm_object *object;
+
+       /* This is an array of all the values of the static members of this
+        * class. */
+       uint8_t *static_values;
+
+       struct list_head static_fixup_site_list;
 };
 
 int vm_class_link(struct vm_class *vmc, const struct cafebabe_class *class);
diff --git a/include/vm/field.h b/include/vm/field.h
index 9639cfe..d98b288 100644
--- a/include/vm/field.h
+++ b/include/vm/field.h
@@ -21,13 +21,12 @@ struct vm_field {
        char *type;
 
        unsigned int offset;
-       unsigned long long static_value;
 };
 
 int vm_field_init(struct vm_field *vmf,
        struct vm_class *vmc, unsigned int field_index);
 void vm_field_init_nonstatic(struct vm_field *vmf, unsigned int offset);
-int vm_field_init_static(struct vm_field *vmf);
+int vm_field_init_static(struct vm_field *vmf, unsigned int offset);
 
 static inline bool vm_field_is_static(struct vm_field *vmf)
 {
@@ -44,4 +43,6 @@ static inline enum vm_type vm_field_type(struct vm_field *vmf)
        return str_to_type(vmf->type);
 }
 
+extern void *static_guard_page;
+
 #endif
diff --git a/include/vm/static.h b/include/vm/static.h
new file mode 100644
index 0000000..d41711d
--- /dev/null
+++ b/include/vm/static.h
@@ -0,0 +1,36 @@
+#ifndef _VM_STATIC_H
+#define _VM_STATIC_H
+
+extern void *static_guard_page;
+
+void static_fixup_init(void);
+
+struct insn;
+struct vm_class;
+
+enum static_fixup_type {
+       STATIC_FIXUP_GETSTATIC,
+       STATIC_FIXUP_PUTSTATIC,
+};
+
+struct static_fixup_site {
+       struct list_head vmc_node;
+       struct list_head cu_node;
+
+       enum static_fixup_type type;
+       struct insn *insn;
+       struct vm_field *vmf;
+       struct compilation_unit *cu;
+};
+
+int add_getstatic_fixup_site(struct insn *insn,
+       struct vm_field *vmf, struct compilation_unit *cu);
+int add_putstatic_fixup_site(struct insn *insn,
+       struct vm_field *vmf, struct compilation_unit *cu);
+
+void fixup_static(struct vm_class *vmc);
+void fixup_static_at(unsigned long addr);
+
+extern unsigned long static_field_signal_bh(unsigned long ret);
+
+#endif
diff --git a/jit/compilation-unit.c b/jit/compilation-unit.c
index 375f481..2f494ef 100644
--- a/jit/compilation-unit.c
+++ b/jit/compilation-unit.c
@@ -65,6 +65,8 @@ struct compilation_unit *compilation_unit_alloc(struct 
vm_method *method)
                cu->exception_spill_slot = get_spill_slot_32(cu->stack_frame);
                if (!cu->exception_spill_slot)
                        goto out_of_memory;
+
+               INIT_LIST_HEAD(&cu->static_fixup_site_list);
        }
 
        return cu;
diff --git a/jit/invoke-bc.c b/jit/invoke-bc.c
index b01f2bc..aa08109 100644
--- a/jit/invoke-bc.c
+++ b/jit/invoke-bc.c
@@ -182,11 +182,6 @@ int convert_invokestatic(struct parse_context *ctx)
        if (!invoke_target)
                return warn("unable to resolve invocation target"), -EINVAL;
 
-       /* JVM Spec. 2nd. ed., §5.5 */
-       err = vm_class_ensure_init(invoke_target->class);
-       if (err)
-               return err;
-
        expr = invoke_expr(invoke_target);
        if (!expr)
                return -ENOMEM;
diff --git a/jit/object-bc.c b/jit/object-bc.c
index a6d6d8f..5ca3d9b 100644
--- a/jit/object-bc.c
+++ b/jit/object-bc.c
@@ -83,17 +83,11 @@ int convert_getstatic(struct parse_context *ctx)
        struct expression *value_dup;
        struct expression *value;
        struct vm_field *fb;
-       int err;
 
        fb = lookup_field(ctx);
        if (!fb)
                return warn("field lookup failed"), -EINVAL;
 
-       /* JVM Spec. 2nd. ed., §5.5 */
-       err = vm_class_ensure_init(fb->class);
-       if (err)
-               return err;
-
        value = class_field_expr(vm_field_type(fb), fb);
        if (!value)
                return -ENOMEM;
@@ -111,17 +105,11 @@ int convert_putstatic(struct parse_context *ctx)
        struct vm_field *fb;
        struct statement *store_stmt;
        struct expression *dest, *src;
-       int err;
 
        fb = lookup_field(ctx);
        if (!fb)
                return warn("field lookup failed"), -EINVAL;
 
-       /* JVM Spec. 2nd. ed., §5.5 */
-       err = vm_class_ensure_init(fb->class);
-       if (err)
-               return err;
-
        src = stack_pop(ctx->bb->mimic_stack);
        dest = class_field_expr(vm_field_type(fb), fb);
        if (!dest)
diff --git a/jit/trampoline.c b/jit/trampoline.c
index fd840e1..8496c74 100644
--- a/jit/trampoline.c
+++ b/jit/trampoline.c
@@ -117,9 +117,13 @@ void *jit_magic_trampoline(struct compilation_unit *cu)
         * Therefore, do fixup for direct call sites unconditionally and fixup
         * vtables if method can be invoked via invokevirtual.
         */
-       if (ret)
+       if (ret) {
                fixup_direct_calls(method->trampoline, (unsigned long) ret);
 
+               if (vm_method_is_static(cu->method))
+                       fixup_static(cu->method->class);
+       }
+
        pthread_mutex_unlock(&cu->mutex);
 
        return ret;
diff --git a/test/arch-x86/Makefile b/test/arch-x86/Makefile
index 9c37de4..3186e30 100644
--- a/test/arch-x86/Makefile
+++ b/test/arch-x86/Makefile
@@ -67,6 +67,7 @@ OBJS = \
        ../../vm/method.o \
        ../../vm/object.o \
        ../../vm/stack.o \
+       ../../vm/static.o \
        ../../vm/types.o \
        ../../vm/utf8.o \
        ../../vm/zalloc.o \
diff --git a/vm/class.c b/vm/class.c
index 9d1d88b..dcad2f0 100644
--- a/vm/class.c
+++ b/vm/class.c
@@ -24,6 +24,7 @@
  * Please refer to the file LICENSE for details.
  */
 
+#include <errno.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
@@ -182,11 +183,18 @@ int vm_class_link(struct vm_class *vmc, const struct 
cafebabe_class *class)
        }
 
        unsigned int offset;
+       unsigned int static_offset;
 
-       if (vmc->super)
+       if (vmc->super) {
                offset = vmc->super->object_size;
-       else
+               static_offset = vmc->super->static_size;
+       } else {
                offset = 0;
+               static_offset = 0;
+       }
+
+       /* XXX: only static fields, right size, etc. */
+       vmc->static_values = malloc(class->fields_count * 8);
 
        for (uint16_t i = 0; i < class->fields_count; ++i) {
                struct vm_field *vmf = &vmc->fields[i];
@@ -197,10 +205,13 @@ int vm_class_link(struct vm_class *vmc, const struct 
cafebabe_class *class)
                }
 
                if (vm_field_is_static(vmf)) {
-                       if (vm_field_init_static(vmf)) {
+                       if (vm_field_init_static(vmf, static_offset)) {
                                NOT_IMPLEMENTED;
                                return -1;
                        }
+
+                       /* XXX: Same as below */
+                       static_offset += 8;
                } else {
                        vm_field_init_nonstatic(vmf, offset);
                        /* XXX: Do field reordering and use the right sizes */
@@ -209,6 +220,7 @@ int vm_class_link(struct vm_class *vmc, const struct 
cafebabe_class *class)
        }
 
        vmc->object_size = offset;
+       vmc->static_size = static_offset;
 
        vmc->methods = malloc(sizeof(*vmc->methods) * class->methods_count);
        if (!vmc->methods) {
@@ -231,6 +243,8 @@ int vm_class_link(struct vm_class *vmc, const struct 
cafebabe_class *class)
        if (!vm_class_is_interface(vmc))
                setup_vtable(vmc);
 
+       INIT_LIST_HEAD(&vmc->static_fixup_site_list);
+
        vmc->state = VM_CLASS_LINKED;
        return 0;
 }
diff --git a/vm/field.c b/vm/field.c
index 252fd52..040dc11 100644
--- a/vm/field.c
+++ b/vm/field.c
@@ -58,15 +58,17 @@ void vm_field_init_nonstatic(struct vm_field *vmf, unsigned 
int offset)
        vmf->offset = offset;
 }
 
-int vm_field_init_static(struct vm_field *vmf)
+int vm_field_init_static(struct vm_field *vmf, unsigned int offset)
 {
+       vmf->offset = offset;
+
        const struct vm_class *vmc = vmf->class;
        const struct cafebabe_class *class = vmc->class;
        const struct cafebabe_field_info *field
                = &class->fields[vmf->field_index];
 
        /* XXX: Actually _use_ the ConstantValue attribute */
-       vmf->static_value = 0;
+       vmf->class->static_values[offset] = 0;
 
        unsigned int constant_value_index = 0;
        if (cafebabe_attribute_array_get(&field->attributes,
diff --git a/vm/jato.c b/vm/jato.c
index 5f5682e..6fcd54f 100644
--- a/vm/jato.c
+++ b/vm/jato.c
@@ -48,6 +48,7 @@
 #include "vm/object.h"
 #include "vm/signal.h"
 #include "vm/stack-trace.h"
+#include "vm/static.h"
 #include "vm/system.h"
 #include "vm/vm.h"
 
@@ -295,6 +296,8 @@ main(int argc, char *argv[])
 
        jit_init_natives();
 
+       static_fixup_init();
+
        /* Search $CLASSPATH last. */
        char *classpath = getenv("CLASSPATH");
        if (classpath)
diff --git a/vm/signal.c b/vm/signal.c
index f4daaea..fb4e09d 100644
--- a/vm/signal.c
+++ b/vm/signal.c
@@ -130,6 +130,12 @@ static void sigsegv_handler(int sig, siginfo_t *si, void 
*ctx)
                return;
        }
 
+       /* Static field access */
+       if (si->si_addr == static_guard_page) {
+               install_signal_bh(ctx, &static_field_signal_bh);
+               return;
+       }
+
  exit:
        print_backtrace_and_die(sig, si, ctx);
 }
diff --git a/vm/static.c b/vm/static.c
new file mode 100644
index 0000000..b49c2f8
--- /dev/null
+++ b/vm/static.c
@@ -0,0 +1,63 @@
+#include <jit/cu-mapping.h>
+#include <jit/exception.h>
+
+#include <vm/class.h>
+#include "vm/guard-page.h"
+#include <vm/object.h>
+#include <vm/signal.h>
+
+#include <arch/instruction.h>
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+void *static_guard_page;
+
+void static_fixup_init(void)
+{
+       static_guard_page = alloc_guard_page();
+       if (!static_guard_page)
+               abort();
+}
+
+int add_static_fixup_site(enum static_fixup_type type, struct insn *insn,
+       struct vm_field *vmf, struct compilation_unit *cu)
+{
+       struct vm_class *vmc;
+       struct static_fixup_site *site;
+
+       vmc = vmf->class;
+
+       site = malloc(sizeof *site);
+       if (!site)
+               return -ENOMEM;
+
+       site->type = type;
+       site->insn = insn;
+       site->vmf = vmf;
+       site->cu = cu;
+       list_add_tail(&site->vmc_node, &vmc->static_fixup_site_list);
+       list_add_tail(&site->cu_node, &cu->static_fixup_site_list);
+       return 0;
+}
+
+int add_getstatic_fixup_site(struct insn *insn,
+       struct vm_field *vmf, struct compilation_unit *cu)
+{
+       return add_static_fixup_site(STATIC_FIXUP_GETSTATIC, insn, vmf, cu);
+}
+
+int add_putstatic_fixup_site(struct insn *insn,
+       struct vm_field *vmf, struct compilation_unit *cu)
+{
+       return add_static_fixup_site(STATIC_FIXUP_PUTSTATIC, insn, vmf, cu);
+}
+
+unsigned long static_field_signal_bh(unsigned long ret)
+{
+       fixup_static_at(ret);
+       return ret;
+}
+
-- 
1.6.0.4


------------------------------------------------------------------------------
_______________________________________________
Jatovm-devel mailing list
Jatovm-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/jatovm-devel

Reply via email to