summaryrefslogtreecommitdiffhomepage
path: root/ir/be/bejit.c
diff options
context:
space:
mode:
authorMatthias Braun <matze@braunis.de>2016-01-31 09:48:26 +0100
committerMatthias Braun <matze@braunis.de>2016-02-01 02:29:11 +0100
commitac88d96aa83ecaa3590906db9ebb169b37578d06 (patch)
tree0160c023c1b7ea4f4df1e46b5c7a99073f3bdc97 /ir/be/bejit.c
parenta80f85566624b9516344239cba28f0eb7c86e8b7 (diff)
bejit: Introduce brand new jit infrastructure
Diffstat (limited to 'ir/be/bejit.c')
-rw-r--r--ir/be/bejit.c372
1 files changed, 372 insertions, 0 deletions
diff --git a/ir/be/bejit.c b/ir/be/bejit.c
new file mode 100644
index 0000000..2fdb1aa
--- /dev/null
+++ b/ir/be/bejit.c
@@ -0,0 +1,372 @@
+/*
+ * This file is part of libFirm.
+ * Copyright (C) 2012 University of Karlsruhe.
+ */
+
+/**
+ * @file
+ * @brief Interface for machine code output
+ * @author Matthias Braun
+ * @date 12.03.2007
+ */
+#include "bejit.h"
+
+#include <assert.h>
+#include <limits.h>
+#include <sys/mman.h>
+
+#include "array.h"
+#include "beemitter.h"
+#include "begnuas.h"
+#include "compiler.h"
+#include "entity_t.h"
+#include "obst.h"
+#include "panic.h"
+
+typedef enum reloc_dest_kind_t {
+ RELOC_DEST_CODE_FRAGMENT,
+ RELOC_DEST_ENTITY,
+} reloc_dest_kind_t;
+
+typedef struct relocation_t {
+ uint8_t be_kind;
+ ENUMBF(reloc_dest_kind_t) dest_kind : 8;
+ uint16_t offset;
+ int32_t dest_offset;
+ union dest {
+ uint16_t fragment_num;
+ ir_entity *entity;
+ } dest;
+} relocation_t;
+
+typedef struct fragment_info_t {
+ unsigned address; /**< Address from begin of code segment */
+ unsigned len; /**< size of the fragments data */
+ uint8_t p2align; /**< power 2 of two we should align */
+ uint8_t max_skip; /**< Maximum number of bytes to skip for alignment */
+ uint16_t n_relocations;
+ relocation_t relocations[];
+} fragment_info_t;
+
+struct ir_jit_segment_t {
+ struct obstack code_obst;
+ struct obstack fragment_info_obst;
+ struct obstack fragment_info_arr_obst;
+};
+
+struct ir_jit_function_t {
+ unsigned size;
+ unsigned n_fragments;
+ char const *code;
+ fragment_info_t **fragment_infos;
+};
+
+struct obstack *code_obst;
+static struct obstack *fragment_info_obst;
+static struct obstack *fragment_info_arr_obst;
+
+ir_jit_segment_t *be_new_jit_segment(void)
+{
+ ir_jit_segment_t *const segment = XMALLOCZ(ir_jit_segment_t);
+ obstack_init(&segment->code_obst);
+ obstack_init(&segment->fragment_info_obst);
+ obstack_init(&segment->fragment_info_arr_obst);
+ return segment;
+}
+
+void be_destroy_jit_segment(ir_jit_segment_t *segment)
+{
+ obstack_free(&segment->code_obst, NULL);
+ obstack_free(&segment->fragment_info_obst, NULL);
+ obstack_free(&segment->fragment_info_arr_obst, NULL);
+ free(segment);
+}
+
+void be_jit_set_entity_addr(ir_entity *entity, void const *address)
+{
+ assert(is_global_entity(entity));
+ entity->attr.global.jit_addr = address;
+}
+
+void const *be_jit_get_entity_addr(ir_entity const *const entity)
+{
+ assert(is_global_entity(entity));
+ return entity->attr.global.jit_addr;
+}
+
+void be_jit_begin_function(ir_jit_segment_t *const segment)
+{
+ assert(obstack_object_size(&segment->code_obst) == 0);
+ assert(obstack_object_size(&segment->fragment_info_obst) == 0);
+ assert(obstack_object_size(&segment->fragment_info_arr_obst) == 0);
+ code_obst = &segment->code_obst;
+ fragment_info_obst = &segment->fragment_info_obst;
+ fragment_info_arr_obst = &segment->fragment_info_arr_obst;
+}
+
+static void layout_fragments(ir_jit_function_t *const function,
+ unsigned const code_size)
+{
+ unsigned const n_fragments = function->n_fragments;
+ fragment_info_t **const fragment_infos = function->fragment_infos;
+
+ unsigned address = 0;
+ unsigned orig_address = 0;
+ for (unsigned i = 0; i < n_fragments; ++i) {
+ fragment_info_t *const fragment = fragment_infos[i];
+ assert(fragment->address == ~0u);
+ assert(fragment->len != ~0u);
+
+ unsigned const align = 1 << fragment->p2align;
+ unsigned const align_mask = ~(align-1);
+ unsigned const aligned = (address + align - 1) & align_mask;
+ if (aligned - address <= fragment->max_skip)
+ address = aligned;
+
+ fragment->address = address;
+
+ address += fragment->len;
+ orig_address += fragment->len;
+ }
+ function->size = address;
+ assert(code_size == orig_address);
+ (void)code_size;
+}
+
+ir_jit_function_t *be_jit_finish_function(void)
+{
+ struct obstack *obst = fragment_info_arr_obst;
+
+ size_t const size = obstack_object_size(obst);
+ unsigned const n_fragments = size / sizeof(fragment_info_t*);
+ assert(size % sizeof(fragment_info_t*) == 0);
+ fragment_info_t **const fragment_infos = obstack_finish(obst);
+
+ unsigned const code_size = obstack_object_size(code_obst);
+
+ ir_jit_function_t *const res = OALLOCZ(obst, ir_jit_function_t);
+ res->n_fragments = n_fragments;
+ res->fragment_infos = fragment_infos;
+ res->code = obstack_finish(code_obst);
+
+ layout_fragments(res, code_size);
+
+#ifndef NDEBUG
+ code_obst = NULL;
+ fragment_info_obst = NULL;
+ fragment_info_arr_obst = NULL;
+#endif
+
+ return res;
+}
+
+unsigned be_get_function_size(ir_jit_function_t const *const function)
+{
+ return function->size;
+}
+
+unsigned be_begin_fragment(uint8_t const p2align, uint8_t const max_skip)
+{
+ assert(obstack_object_size(fragment_info_obst) == 0);
+
+ fragment_info_t const fragment = {
+ .address = obstack_object_size(code_obst),
+ .len = ~0u,
+ .p2align = p2align,
+ .max_skip = max_skip,
+ };
+ obstack_grow(fragment_info_obst, &fragment, sizeof(fragment));
+
+ return obstack_object_size(fragment_info_arr_obst)/sizeof(fragment_info_t*);
+}
+
+void be_finish_fragment(void)
+{
+ size_t size = obstack_object_size(fragment_info_obst);
+ assert(size >= sizeof(fragment_info_t));
+
+ fragment_info_t *const fragment = obstack_finish(fragment_info_obst);
+ obstack_ptr_grow(fragment_info_arr_obst, fragment);
+
+ unsigned const begin = fragment->address;
+ unsigned const end = obstack_object_size(code_obst);
+ fragment->len = end-begin;
+ fragment->n_relocations
+ = (size - sizeof(fragment_info_t)) / sizeof(relocation_t);
+
+#ifndef NDEBUG
+ assert(sizeof(fragment_info_t)
+ + fragment->n_relocations * sizeof(relocation_t) == size);
+ fragment->address = ~0u;
+#endif
+}
+
+static void be_emit_relocation(unsigned const len, relocation_t *const relocation)
+{
+ fragment_info_t *const fragment = obstack_base(fragment_info_obst);
+ unsigned const begin = fragment->address;
+ unsigned const now = obstack_object_size(code_obst);
+ relocation->offset = now - begin;
+
+ assert(obstack_object_size(fragment_info_obst) >= sizeof(fragment_info_t));
+ obstack_grow(fragment_info_obst, relocation, sizeof(*relocation));
+
+ obstack_blank(code_obst, len);
+}
+
+void be_emit_reloc_fragment(unsigned const len, uint8_t const be_kind,
+ unsigned const fragment_num, int32_t const offset)
+{
+ relocation_t relocation = {
+ .be_kind = be_kind,
+ .dest_kind = RELOC_DEST_CODE_FRAGMENT,
+ .dest_offset = offset,
+ .dest.fragment_num = fragment_num,
+ };
+ be_emit_relocation(len, &relocation);
+}
+
+void be_emit_reloc_entity(unsigned const len, uint8_t be_kind,
+ ir_entity *const entity, int32_t const offset)
+{
+ relocation_t relocation = {
+ .be_kind = be_kind,
+ .dest_kind = RELOC_DEST_ENTITY,
+ .dest_offset = offset,
+ .dest.entity = entity,
+ };
+ be_emit_relocation(len, &relocation);
+}
+
+static int32_t resolve_relocation_code(ir_jit_function_t const *const function,
+ relocation_t const *const relocation,
+ unsigned const relocation_address)
+{
+ unsigned const fragment_num = relocation->dest.fragment_num;
+ assert(fragment_num < function->n_fragments);
+ fragment_info_t const *const fragment
+ = function->fragment_infos[fragment_num];
+ unsigned const dest_address = fragment->address + relocation->dest_offset;
+ return (int32_t)dest_address - relocation_address;
+}
+
+static unsigned emit_relocation(ir_jit_function_t const *const function,
+ relocation_t const *const relocation,
+ unsigned const relocation_address,
+ char *const relocation_abs,
+ emit_relocation_func const emit)
+{
+ switch (relocation->dest_kind) {
+ case RELOC_DEST_CODE_FRAGMENT: {
+ int32_t const dest = resolve_relocation_code(function, relocation,
+ relocation_address);
+ return emit(relocation_abs, relocation->be_kind, NULL, dest);
+ }
+ case RELOC_DEST_ENTITY:
+ return emit(relocation_abs, relocation->be_kind,
+ relocation->dest.entity, relocation->dest_offset);
+ }
+ panic("Invalid relocation");
+}
+
+static void emit_bytes_as_asm(char const *const begin, char const *const end)
+{
+ assert(begin <= end);
+ for (char const *b = begin; b < end; ++b) {
+ be_emit_irprintf("\t.byte 0x%02X\n", (uint8_t)*b);
+ be_emit_write_line();
+ }
+}
+
+static void emit_fragment_as_asm(ir_jit_function_t const *const function,
+ fragment_info_t const *const fragment,
+ char const *const fragment_code,
+ emit_relocation_func const emit)
+{
+ unsigned const fragment_address = fragment->address;
+ char const * b = fragment_code;
+ for (unsigned r = 0, n = fragment->n_relocations; r < n; ++r) {
+ relocation_t const *const relocation = &fragment->relocations[r];
+ unsigned const offset = relocation->offset;
+ emit_bytes_as_asm(b, fragment_code + offset);
+ unsigned const reloc_address = fragment_address + offset;
+ unsigned const reloc_size
+ = emit_relocation(function, relocation, reloc_address, NULL, emit);
+ b = fragment_code + relocation->offset + reloc_size;
+ }
+ char const *const end = fragment_code + fragment->len;
+ emit_bytes_as_asm(b, end);
+}
+
+void be_jit_emit_as_asm(ir_jit_function_t *const function,
+ emit_relocation_func const emit)
+{
+ /* Move fragments to their final addresses */
+ char const *const code = function->code;
+ unsigned orig_address = 0;
+ unsigned last_address = 0;
+ for (size_t i = 0, n = function->n_fragments; i < n; ++i) {
+ fragment_info_t const *const fragment = function->fragment_infos[i];
+ unsigned const address = fragment->address;
+ if (address > last_address)
+ be_emit_irprintf("\t.p2align %u,,%u\n", fragment->p2align,
+ fragment->max_skip);
+
+ emit_fragment_as_asm(function, fragment, code + orig_address, emit);
+
+ orig_address += fragment->len;
+ last_address = address + fragment->len;
+ }
+}
+
+static void emit_fragment(ir_jit_function_t const *const function,
+ fragment_info_t const *const fragment,
+ char const *const fragment_code, char *const buffer,
+ emit_relocation_func const emit)
+{
+ unsigned const fragment_address = fragment->address;
+ char const * b = fragment_code;
+ char * d = buffer;
+ unsigned last_offset = 0;
+ for (unsigned r = 0, n = fragment->n_relocations; r < n; ++r) {
+ relocation_t const *const relocation = &fragment->relocations[r];
+ unsigned const offset = relocation->offset;
+ unsigned const len = offset - last_offset;
+ assert(last_offset <= offset);
+ memcpy(d, b, len);
+ d += len;
+ b += len;
+ unsigned const reloc_address = fragment_address + offset;
+ unsigned const reloc_size
+ = emit_relocation(function, relocation, reloc_address, d, emit);
+ d += reloc_size;
+ b += reloc_size;
+ last_offset = offset + reloc_size;
+ }
+ char const *const end = fragment_code + fragment->len;
+ assert(b <= end);
+ memcpy(d, b, end-b);
+}
+
+void be_jit_emit_memory(char *const buffer, ir_jit_function_t *const function,
+ be_jit_emit_interface_t const *const emitter)
+{
+ /* Copy fragments and resolve relocations. */
+ char const *const code = function->code;
+ unsigned orig_address = 0;
+ unsigned last_address = 0;
+ for (size_t i = 0, n = function->n_fragments; i < n; ++i) {
+ fragment_info_t const *const fragment = function->fragment_infos[i];
+ unsigned const address = fragment->address;
+ unsigned const nop_bytes = last_address - address;
+ assert(address >= last_address);
+ if (nop_bytes > 0)
+ emitter->nops(buffer + last_address, nop_bytes);
+
+ emit_fragment(function, fragment, code+orig_address, buffer+address,
+ emitter->relocation);
+
+ orig_address += fragment->len;
+ last_address = address + fragment->len;
+ }
+}