Commit ac88d96a authored by Matthias Braun's avatar Matthias Braun
Browse files

bejit: Introduce brand new jit infrastructure

parent a80f8556
libFirm 1.22.1 (2016-01-07)
---------------------------
* Fix cmake/make build
* New just in time compilation mode which compiles into a memory buffer (ia32)
* Support PIC with PLT for ELF (amd64)
* Add `ia32-get_ip={pop,thunk}`
* Generate 'mov $~1, %r; rol x, %r' for '~(1 << x)' (ia32)
......
/*
* This file is part of libFirm.
* Copyright (C) 2015 Matthias Braun
*/
/**
* @file
* @brief Just in time compilation
*/
#ifndef FIRM_JIT_H
#define FIRM_JIT_H
#include "firm_types.h"
#include "begin.h"
/**
* @ingroup be
* @defgroup jit Just in Time Compilation
*
* Provides interface to generate code and resolve symbols in memory buffers.
* This is often called just in time compilation.
* @{
*/
/**
* Just in time compilation environment. Holds memory for several \ref
* ir_jit_function_t.
*/
typedef struct ir_jit_segment_t ir_jit_segment_t;
/**
* Just in time compiled function with potentially unresolved relocations.
*/
typedef struct ir_jit_function_t ir_jit_function_t;
/**
* Create a new jit segment.
*/
FIRM_API ir_jit_segment_t *be_new_jit_segment(void);
/**
* Destroy jit segment \p segment. Invalidates references to functions created in
* the segment.
*/
FIRM_API void be_destroy_jit_segment(ir_jit_segment_t *segment);
/**
* Set absolute address of global entities so relocations in jit compiled
* code an be resolved.
*/
FIRM_API void be_jit_set_entity_addr(ir_entity *entity, void const *address);
/**
* Return previously set address. \see be_jit_set_entity_addr()
*/
FIRM_API void const *be_jit_get_entity_addr(ir_entity const *entity);
/**
* Compile graph \p irg to a sequence of machine instructions and relocations.
*/
FIRM_API ir_jit_function_t *be_jit_compile(ir_jit_segment_t *segment,
ir_graph *irg);
/**
* Return the buffer size necessary to emit \p function with be_emit_function().
*/
FIRM_API unsigned be_get_function_size(ir_jit_function_t const *function);
/**
* Emit \p function into \p buffer and resolve symbols and relocations.
*/
FIRM_API void be_emit_function(char *buffer, ir_jit_function_t *function);
/** @} */
#include "end.h"
#endif
......@@ -14,6 +14,7 @@
#include <stdbool.h>
#include "firm_types.h"
#include "jit.h"
#include "raw_bitset.h"
#include "be_types.h"
......@@ -288,6 +289,10 @@ struct arch_isa_if_t {
*/
void (*generate_code)(FILE *output, const char *cup_name);
ir_jit_function_t* (*jit_compile)(ir_jit_segment_t *segment, ir_graph *irg);
void (*emit_function)(char *buffer, ir_jit_function_t *function);
/**
* lowers current program for target. See the documentation for
* be_lower_for_target() for details.
......
/*
* 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 <assert.h>
#include <limits.h>
#include "beemitter_binary.h"
#include "obst.h"
#include "panic.h"
static code_fragment_t *first_fragment;
static code_fragment_t *last_fragment;
#ifndef NDEBUG
static const unsigned CODE_FRAGMENT_MAGIC = 0x4643414d; /* "CFMA" */
#endif
struct obstack code_fragment_obst;
/** returns current fragment (the address stays only valid until the next
be_emit(8/16/32/entity) call!) */
code_fragment_t *be_get_current_fragment(void)
{
code_fragment_t *fragment = (code_fragment_t*)obstack_base(&code_fragment_obst);
assert(obstack_object_size(&code_fragment_obst) >= sizeof(code_fragment_t));
assert(fragment->magic == CODE_FRAGMENT_MAGIC);
return fragment;
}
/** allocates a new fragment on the obstack (warning: address is only valid
till next be_emit */
static void alloc_fragment(void)
{
code_fragment_t *fragment;
/* shouldn't have any growing fragments */
assert(obstack_object_size(&code_fragment_obst) == 0);
obstack_blank(&code_fragment_obst, sizeof(*fragment));
fragment = (code_fragment_t*)obstack_base(&code_fragment_obst);
memset(fragment, 0, sizeof(*fragment));
#ifndef NDEBUG
fragment->magic = CODE_FRAGMENT_MAGIC;
#endif
fragment->len = 0;
fragment->alignment = 1;
fragment->offset = 0;
fragment->max_offset = UINT_MAX;
}
static code_fragment_t *finish_fragment(void)
{
code_fragment_t *fragment = be_get_current_fragment();
fragment->len
= obstack_object_size(&code_fragment_obst) - sizeof(*fragment);
fragment = (code_fragment_t*) obstack_finish(&code_fragment_obst);
last_fragment = fragment;
if (first_fragment == NULL)
first_fragment = fragment;
return fragment;
}
void be_start_code_emitter(void)
{
obstack_init(&code_fragment_obst);
first_fragment = NULL;
alloc_fragment();
}
void be_start_new_fragment(void)
{
finish_fragment();
alloc_fragment();
}
static void emit(FILE *file, const unsigned char *buffer, size_t len)
{
size_t i;
for (i = 0; i < len; ++i) {
size_t i2;
fputs("\t.byte ", file);
for (i2 = i; i2 < i + 30 && i2 < len; ++i2) {
fprintf(file, "0x%02X", (unsigned)buffer[i2]);
}
i = i2;
fputs("\n", file);
}
}
static unsigned align(unsigned offset, unsigned alignment)
{
if (offset % alignment != 0) {
offset += alignment - (offset % alignment);
}
return offset;
}
static bool determine_jumpsize_iteration(
const binary_emiter_interface_t *interface)
{
unsigned offset = 0;
unsigned max_offset = 0;
bool changed = false;
code_fragment_t *fragment;
for (fragment = first_fragment; fragment != NULL;
fragment = fragment->next) {
unsigned alignment = fragment->alignment;
/* assure alignment */
offset = align(offset, alignment);
max_offset = align(max_offset, alignment);
if (offset != fragment->offset) {
changed = true;
fragment->offset = offset;
}
fragment->max_offset = max_offset;
/* advance offset */
offset += fragment->len;
max_offset += fragment->len;
interface->determine_jumpsize(fragment);
offset += fragment->jumpsize_min;
max_offset += fragment->jumpsize_max;
}
return changed;
}
static void determine_offsets(const binary_emiter_interface_t *interface)
{
bool changed;
assert(first_fragment->alignment == 1);
first_fragment->offset = 0;
first_fragment->max_offset = 0;
/* The algorithm calculates a lower and upper bound for the offset of each
* fragment. With this information we can calculate a lower and upper bound
* for the size of each jump instruction.
* A single iteration updates the offset bounds for all fragments and jump
* sizes for each fragment. We iterate until we had an iteration where
* none of the minimum offsets changed. */
do {
changed = determine_jumpsize_iteration(interface);
/* TODO: we should have an abort mode for the case when the offsets
don't converge fast enough. We could simply use a pessimistic
solution after a few iterations... */
} while (changed);
}
void be_emit_entity(ir_entity *entity, bool entity_sign, int offset,
bool is_relative)
{
(void) entity;
(void) entity_sign;
(void) offset;
(void) is_relative;
panic("not implemented yet");
}
void be_emit_code(FILE *output, const binary_emiter_interface_t *interface)
{
unsigned offset;
code_fragment_t *fragment;
finish_fragment();
/* determine near/far jumps */
determine_offsets(interface);
/* emit code */
offset = 0;
for (fragment = first_fragment; fragment != NULL;
fragment = fragment->next) {
unsigned char *jmpbuffer;
unsigned nops;
/* assure alignment by emitting nops */
assert(fragment->offset >= offset);
nops = fragment->offset - offset;
if (nops > 0) {
unsigned char *nopbuffer = (unsigned char*)obstack_alloc(&code_fragment_obst, nops);
interface->create_nops(nopbuffer, nops);
emit(output, nopbuffer, nops);
offset = fragment->offset;
obstack_free(&code_fragment_obst, nopbuffer);
}
/* emit the fragment */
emit(output, fragment->data, fragment->len);
offset += fragment->len;
/* emit the jump */
jmpbuffer = (unsigned char*)obstack_alloc(&code_fragment_obst, fragment->jumpsize_min);
interface->emit_jump(fragment, jmpbuffer);
emit(output, jmpbuffer, fragment->jumpsize_min);
offset += fragment->jumpsize_min;
obstack_free(&code_fragment_obst, jmpbuffer);
}
}
/*
* This file is part of libFirm.
* Copyright (C) 2012 University of Karlsruhe.
*/
/**
* @file
* @brief Interface for binary machine code output.
* @author Matthias Braun
* @date 12.03.2007
*/
#ifndef FIRM_BE_BEEMITTER_BINARY_H
#define FIRM_BE_BEEMITTER_BINARY_H
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include "firm_types.h"
#include "obst.h"
extern struct obstack code_fragment_obst;
typedef struct code_fragment_t code_fragment_t;
struct code_fragment_t {
#ifndef NDEBUG
unsigned magic;
#endif
unsigned len; /**< size of the fragments data (the jump is
included in the data if its size is
unknown) */
unsigned alignment; /**< alignment of the fragment in bytes */
code_fragment_t *next; /**< pointer to next fragment in line */
unsigned offset; /**< offset of this fragment relative to the
"start" */
unsigned max_offset; /**< maximum offset */
int jump_type; /**< can be used by the backend to indicate
the type of jump at the end of this
fragment */
void *jump_data; /**< can be used by the backend to encode
additional data for the jump (like its
destination) */
code_fragment_t *destination; /**< destination fragment of this jump */
unsigned short jumpsize_min;
unsigned short jumpsize_max;
unsigned char data[]; /**< data starts here */
};
typedef struct binary_emiter_interface_t binary_emiter_interface_t;
struct binary_emiter_interface_t {
/** create @p size of NOP instructions for alignment */
void (*create_nops) (unsigned char *buffer, unsigned size);
/**
* emits a jump by writing it to the buffer. The code for the jump must be
* exactly fragment->jumpsize_min bytes long.
* TODO: create a stream API which we can use here...
*/
void (*emit_jump) (code_fragment_t *fragment, unsigned char *buffer);
/**
* determines the minimum and maximum size of bytes needed to emit
* the jump at the end of the fragment. The function must update the
* jumpsize_min and jumpsize_max fields in the fragment.
*/
void (*determine_jumpsize) (code_fragment_t *fragment);
};
/** Initializes and prepares for emitting a routine with code "fragments".
* Creates an initial fragment in which you can emit right away.
*/
void be_start_code_emitter(void);
/** finalizes and emits current procedure */
void be_emit_code(FILE *output, const binary_emiter_interface_t *interface);
/** finish current fragment and return its final address */
code_fragment_t *be_finish_fragment(void);
/** Create a new code fragment (and append it to the previous one) */
void be_start_new_fragment(void);
/** returns current fragment (the address stays only valid until the next
be_emit(8/16/32/entity) call!) */
code_fragment_t *be_get_current_fragment(void);
/** appends a byte to the current fragment */
static inline void be_emit8(const unsigned char byte)
{
obstack_1grow(&code_fragment_obst, byte);
}
/** appends a word (16bits) to the current fragment */
static inline void be_emit16(const uint16_t u16)
{
/* TODO: fix endianess if needed */
obstack_grow(&code_fragment_obst, &u16, 2);
}
/** appends a dword (32bits) to the current fragment */
static inline void be_emit32(const uint32_t u32)
{
/* TODO: fix endianess if needed */
obstack_grow(&code_fragment_obst, &u32, 4);
}
/** leave space where an entity reference is put at the finish stage */
void be_emit_entity(ir_entity *entity, bool entity_sign, int offset,
bool is_relative);
#endif
/*
* 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;