Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
Zwinkau
libfirm
Commits
ac88d96a
Commit
ac88d96a
authored
Jan 31, 2016
by
Matthias Braun
Browse files
bejit: Introduce brand new jit infrastructure
parent
a80f8556
Changes
10
Hide whitespace changes
Inline
Side-by-side
NEWS.md
View file @
ac88d96a
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)
...
...
include/libfirm/jit.h
0 → 100644
View file @
ac88d96a
/*
* 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
ir/be/bearch.h
View file @
ac88d96a
...
...
@@ -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.
...
...
ir/be/beemitter_binary.c
deleted
100644 → 0
View file @
a80f8556
/*
* 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
);
}
}
ir/be/beemitter_binary.h
deleted
100644 → 0
View file @
a80f8556
/*
* 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
ir/be/bejit.c
0 → 100644
View file @
ac88d96a
/*
* 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
;