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
165e48ff
Commit
165e48ff
authored
May 17, 2005
by
Sebastian Hack
Browse files
Cosmetic changes to the chordal register allocator
Implemented tree intrrval dumping for chordal graphs.
parent
92d0a026
Changes
19
Hide whitespace changes
Inline
Side-by-side
ir/be/Makefile.in
View file @
165e48ff
...
...
@@ -24,7 +24,8 @@ SOURCES += Makefile.in besched.h belistsched.h belistsched.c \
benumb_t.h benumb.c bechordal.c bera.c beutil.c
\
bera.h bechordalspill.c beasm_dump_globals.c beasm_asm_gnu.c
\
sp_matrix.c becopyoptmain.c becopyopt.c becopyheur.c
\
becopyilp.c becopystat.c bearch_firm.c bearch.c
becopyilp.c becopystat.c bearch_firm.c bearch.c bechordal_draw.c
\
bechordal_draw.h
include
$(topdir)/MakeRules
...
...
ir/be/bearch.c
View file @
165e48ff
...
...
@@ -95,6 +95,14 @@ int arch_get_allocatable_regs(const arch_env_t *env, const ir_node *irn,
return
0
;
}
int
arch_is_register_operand
(
const
arch_env_t
*
env
,
const
ir_node
*
irn
,
int
pos
)
{
const
arch_irn_ops_t
*
ops
=
get_irn_ops
(
env
,
irn
);
const
arch_register_req_t
*
req
=
ops
->
get_irn_reg_req
(
irn
,
pos
);
return
req
!=
NULL
;
}
int
arch_reg_is_allocatable
(
const
arch_env_t
*
env
,
const
ir_node
*
irn
,
int
pos
,
const
arch_register_t
*
reg
)
{
...
...
ir/be/bearch.h
View file @
165e48ff
...
...
@@ -293,6 +293,17 @@ typedef struct _arch_irn_ops_t {
}
arch_irn_ops_t
;
/**
* Check if an operand is a register operand.
* @param env The environment.
* @param irn The node.
* @param pos The position of the operand.
* @return 1, if the operand is significant for register allocation, 0
* if not.
*/
extern
int
arch_is_register_operand
(
const
arch_env_t
*
env
,
const
ir_node
*
irn
,
int
pos
);
/**
* Get the number of allocatable registers concerning
* a register class for an operand of a node.
...
...
ir/be/bechordal.c
View file @
165e48ff
...
...
@@ -29,20 +29,22 @@
#include
"beutil.h"
#include
"besched.h"
#include
"bera_t.h"
#include
"benumb_t.h"
#include
"besched_t.h"
#include
"belive_t.h"
#include
"bechordal_t.h"
#include
"bearch.h"
#define BUILD_GRAPH
#undef DUMP_INTERVALS
#include
"bechordal_t.h"
#include
"bechordal_draw.h"
#define NO_COLOR (-1)
#define DUMP_INTERVALS
#undef DUMP_PRESSURE
#undef DUMP_IFG
#if defined(DUMP_IFG) && !defined(BUILD_GRAPH)
#define BUILD_GRAPH
#
error Must
define BUILD_GRAPH
to be able to dump it.
#endif
...
...
@@ -54,186 +56,8 @@
#endif
/* DEBUG_libfirm */
#define TEST_COLORS 2048
static
firm_dbg_module_t
*
dbg
;
/**
* Environment for each of the chordal register allocator phases
*/
typedef
struct
_env_t
{
struct
obstack
obst
;
/**< An obstack for temporary storage. */
ir_graph
*
irg
;
/**< The graph the reg alloc is running on. */
#ifdef BUILD_GRAPH
set
*
nodes
;
/**< The interference graph nodes. */
set
*
edges
;
/**< The interference graph edges. */
#endif
bitset_t
*
live
;
/**< A liveness bitset. */
bitset_t
*
colors
;
/**< The color mask. */
bitset_t
*
in_colors
;
/**< Colors used by live in values. */
int
colors_n
;
/**< The number of colors. */
const
arch_env_t
*
arch_env
;
/**< The arch interface environment. */
const
arch_register_class_t
*
cls
;
/**< The current register class. */
void
*
data
;
/**< Some pointer, to which different
phases can attach data to. */
}
env_t
;
typedef
struct
_be_chordal_dump_params_t
{
int
x_dist
;
int
y_dist
;
double
font_scale
;
}
be_chordal_dump_params_t
;
static
const
be_chordal_dump_params_t
dump_params
=
{
10
,
10
,
4
};
#ifdef DUMP_INTERVALS
static
INLINE
void
intv_filename
(
char
*
s
,
size_t
n
,
const
env_t
*
env
,
ir_node
*
block
)
{
ir_snprintf
(
s
,
n
,
"intv_%F_%s_bl%N.eps"
,
env
->
irg
,
env
->
cls
->
name
,
block
);
}
static
void
draw_interval_graph
(
const
env_t
*
env
,
ir_node
*
block
,
const
be_chordal_dump_params_t
*
params
)
{
int
i
;
int
x_dist
=
params
->
x_dist
;
int
y_dist
=
params
->
y_dist
;
ir_graph
*
irg
=
env
->
irg
;
struct
list_head
*
border_head
=
get_block_border_head
(
block
);
FILE
*
f
;
char
buf
[
1024
];
intv_filename
(
buf
,
sizeof
(
buf
),
env
,
block
);
if
((
f
=
fopen
(
buf
,
"wt"
))
!=
NULL
)
{
border_t
*
b
;
int
*
seen
=
xcalloc
(
get_graph_node_count
(
irg
),
sizeof
(
seen
[
0
]));
int
last_pos
=
list_empty
(
border_head
)
?
0
:
list_entry
(
border_head
->
prev
,
border_t
,
list
)
->
step
;
int
max_col
=
0
;
list_for_each_entry_reverse
(
border_t
,
b
,
border_head
,
list
)
{
const
ir_node
*
irn
=
b
->
irn
;
int
col
=
get_irn_color
(
irn
);
max_col
=
max_col
>
col
?
max_col
:
col
;
}
fprintf
(
f
,
"%%!PS-Adobe-2.0
\n
"
);
fprintf
(
f
,
"%%%%BoundingBox: -10 -10 %d %d
\n
"
,
x_dist
*
last_pos
+
x_dist
,
y_dist
*
max_col
+
y_dist
);
fprintf
(
f
,
"/mainfont /Courier findfont %f scalefont def
\n
"
,
params
->
font_scale
);
fprintf
(
f
,
"mainfont setfont
\n
"
);
fprintf
(
f
,
"0.2 setlinewidth
\n
"
);
for
(
i
=
0
;
i
<=
last_pos
;
++
i
)
{
fprintf
(
f
,
"0 0 0 setrgbcolor
\n
"
);
fprintf
(
f
,
"%d %d moveto
\n
"
,
i
*
x_dist
,
-
2
);
fprintf
(
f
,
"%d %d lineto
\n
"
,
i
*
x_dist
,
max_col
*
y_dist
+
2
);
fprintf
(
f
,
"stroke
\n
"
);
}
fprintf
(
f
,
"0.5 setlinewidth
\n
"
);
list_for_each_entry_reverse
(
border_t
,
b
,
border_head
,
list
)
{
const
ir_node
*
irn
=
b
->
irn
;
int
nr
=
get_irn_graph_nr
(
irn
);
if
(
b
->
is_def
)
seen
[
nr
]
=
b
->
step
;
else
{
int
col
=
get_irn_color
(
irn
);
int
pos
=
last_pos
-
seen
[
nr
];
int
end_pos
=
last_pos
-
b
->
step
;
int
live_in
=
is_live_in
(
block
,
irn
);
int
live_end
=
is_live_end
(
block
,
irn
);
int
y_val
=
y_dist
*
col
;
int
red
=
0
;
int
green
=
live_end
;
int
blue
=
live_in
;
fprintf
(
f
,
"0 0 0 setrgbcolor
\n
"
);
fprintf
(
f
,
"%d %d moveto
\n
"
,
x_dist
*
pos
+
2
,
y_val
+
2
);
ir_fprintf
(
f
,
"(%n/%d%s) show
\n
"
,
irn
,
nr
,
is_phi_operand
(
irn
)
?
"*"
:
""
);
fprintf
(
f
,
"%d %d %d setrgbcolor
\n
"
,
red
,
green
,
blue
);
fprintf
(
f
,
"%d %d moveto
\n
"
,
x_dist
*
pos
,
y_val
);
fprintf
(
f
,
"%d %d lineto
\n
"
,
(
x_dist
*
end_pos
)
-
5
,
y_val
);
fprintf
(
f
,
"stroke
\n
"
);
}
}
free
(
seen
);
fclose
(
f
);
}
}
static
void
dump_block
(
ir_node
*
bl
,
void
*
data
)
{
const
env_t
*
env
=
data
;
FILE
*
f
=
env
->
data
;
char
buf
[
128
];
draw_interval_graph
(
env
,
bl
,
&
dump_params
);
intv_filename
(
buf
,
sizeof
(
buf
),
env
,
bl
);
ir_fprintf
(
f
,
"
\t
b%N [shape=
\"
epsf
\"
shapefile=
\"
%s
\"
];
\n
"
,
bl
,
buf
);
}
static
void
dump_edges
(
ir_node
*
bl
,
void
*
data
)
{
const
env_t
*
env
=
data
;
FILE
*
f
=
env
->
data
;
int
i
,
n
;
ir_node
*
dom
;
#if 0
for(i = 0, n = get_irn_arity(bl); i < n; ++i) {
ir_node *pred = get_irn_n(bl, i);
ir_fprintf(f, "\tb%N -> b%N;\n", get_nodes_block(pred), bl);
}
#endif
for
(
dom
=
get_Block_dominated_first
(
bl
);
dom
;
dom
=
get_Block_dominated_next
(
dom
))
{
ir_fprintf
(
f
,
"
\t
b%N -> b%N;
\n
"
,
dom
,
bl
);
}
}
static
void
dump_intv_cfg
(
env_t
*
env
)
{
FILE
*
f
;
char
buf
[
128
];
ir_snprintf
(
buf
,
sizeof
(
buf
),
"intv_cfg_%s_%F.dot"
,
env
->
cls
->
name
,
env
->
irg
);
if
((
f
=
fopen
(
buf
,
"wt"
))
!=
NULL
)
{
void
*
old_data
=
env
->
data
;
env
->
data
=
f
;
ir_fprintf
(
f
,
"digraph G {
\n
"
);
ir_fprintf
(
f
,
"
\t
graph [rankdir=
\"
LR
\"
, ordering=
\"
out
\"
];
\n
"
);
dom_tree_walk_irg
(
env
->
irg
,
dump_block
,
dump_edges
,
env
);
// irg_block_walk_graph(env->irg, dump_block, dump_edges, env);
ir_fprintf
(
f
,
"}
\n
"
);
fclose
(
f
);
env
->
data
=
old_data
;
}
}
#endif
#ifdef BUILD_GRAPH
#define IF_EDGE_HASH(e) ((e)->src)
...
...
@@ -269,7 +93,7 @@ static INLINE if_edge_t *edge_init(if_edge_t *edge, int src, int tgt)
return
edge
;
}
static
INLINE
void
add_if
(
const
env_t
*
env
,
int
src
,
int
tgt
)
static
INLINE
void
add_if
(
const
be_chordal_
env_t
*
env
,
int
src
,
int
tgt
)
{
if_edge_t
edge
;
if_node_t
node
,
*
src_node
,
*
tgt_node
;
...
...
@@ -290,18 +114,20 @@ static INLINE void add_if(const env_t *env, int src, int tgt)
pset_insert_ptr
(
tgt_node
->
neighb
,
src_node
);
}
static
INLINE
int
are_connected
(
const
env_t
*
env
,
int
src
,
int
tgt
)
static
INLINE
int
are_connected
(
const
be_chordal_
env_t
*
env
,
int
src
,
int
tgt
)
{
if_edge_t
edge
;
edge_init
(
&
edge
,
src
,
tgt
);
return
set_find
(
env
->
edges
,
&
edge
,
sizeof
(
edge
),
IF_EDGE_HASH
(
&
edge
))
!=
NULL
;
}
int
ifg_has_edge
(
const
ir_graph
*
irg
,
if_node_t
*
n1
,
if_node_t
*
n2
)
{
return
are_connected
(
get_irg_ra_link
(
irg
)
,
n1
->
nnr
,
n2
->
nnr
);
int
ifg_has_edge
(
const
be_chordal_env_t
*
env
,
const
if_node_t
*
n1
,
const
if_node_t
*
n2
)
{
return
are_connected
(
env
,
n1
->
nnr
,
n2
->
nnr
);
}
static
void
dump_ifg
(
ir_graph
*
irg
,
set
*
edges
,
const
char
*
filename
)
#ifdef DUMP_IFG
static
void
dump_ifg
(
const
be_chordal_env_t
*
env
)
{
static
const
char
*
colors
[]
=
{
"coral"
,
...
...
@@ -386,6 +212,11 @@ static void dump_ifg(ir_graph *irg, set *edges, const char *filename)
static
const
int
n_colors
=
sizeof
(
colors
)
/
sizeof
(
colors
[
0
]);
FILE
*
f
;
set
*
edges
=
env
->
edges
;
ir_graph
*
irg
=
env
->
irg
;
char
filename
[
128
];
ir_snprintf
(
filename
,
sizeof
(
filename
),
"ifg_%s_%F.dot"
,
env
->
cls
->
name
,
irg
);
if
((
f
=
fopen
(
filename
,
"wt"
))
!=
NULL
)
{
bitset_pos_t
pos
;
...
...
@@ -425,6 +256,8 @@ static void dump_ifg(ir_graph *irg, set *edges, const char *filename)
}
#endif
/* DUMP_IFG */
#endif
/* BUILD_GRAPH */
...
...
@@ -440,7 +273,7 @@ static void dump_ifg(ir_graph *irg, set *edges, const char *filename)
* @param is_def Is the border a use or a def.
* @return The created border.
*/
static
INLINE
border_t
*
border_add
(
env_t
*
env
,
struct
list_head
*
head
,
static
INLINE
border_t
*
border_add
(
be_chordal_
env_t
*
env
,
struct
list_head
*
head
,
ir_node
*
irn
,
unsigned
step
,
unsigned
pressure
,
unsigned
is_def
,
unsigned
is_real
)
{
...
...
@@ -510,7 +343,7 @@ static void pressure(ir_node *block, void *env_ptr)
#define border_use(irn, step, real) \
border_add(env, head, irn, step, ++pressure, 0, real)
env_t
*
env
=
env_ptr
;
be_chordal_
env_t
*
env
=
env_ptr
;
bitset_t
*
live
=
env
->
live
;
ir_node
*
irn
;
...
...
@@ -526,8 +359,9 @@ static void pressure(ir_node *block, void *env_ptr)
bitset_clear_all
(
live
);
/* Set up the border list in the block info */
head
=
&
get_ra_block_info
(
block
)
->
border_
head
;
head
=
obstack_alloc
(
&
env
->
obst
,
sizeof
(
*
head
))
;
INIT_LIST_HEAD
(
head
);
pmap_insert
(
env
->
border_heads
,
block
,
head
);
/*
* Make final uses of all values live out of the block.
...
...
@@ -550,9 +384,6 @@ static void pressure(ir_node *block, void *env_ptr)
DBG
((
dbg
,
LEVEL_1
,
"
\t
insn: %n, pressure: %d
\n
"
,
irn
,
pressure
));
DBG
((
dbg
,
LEVEL_2
,
"
\t
live: %b
\n
"
,
live
));
/* Erase the color of each node encountered. */
set_irn_color
(
irn
,
NO_COLOR
);
/*
* If the node defines some value, which can put into a
* register of the current class, make a border for it.
...
...
@@ -610,7 +441,7 @@ static void pressure(ir_node *block, void *env_ptr)
static
void
assign
(
ir_node
*
block
,
void
*
env_ptr
)
{
env_t
*
env
=
env_ptr
;
be_chordal_
env_t
*
env
=
env_ptr
;
struct
obstack
*
obst
=
&
env
->
obst
;
bitset_t
*
live
=
env
->
live
;
bitset_t
*
colors
=
env
->
colors
;
...
...
@@ -623,7 +454,7 @@ static void assign(ir_node *block, void *env_ptr)
const
ir_node
*
irn
;
border_t
*
b
;
struct
list_head
*
head
=
&
get_
ra_
block_
info
(
block
)
->
border_head
;
struct
list_head
*
head
=
get_block_
border_head
(
env
,
block
)
;
pset
*
live_in
=
get_live_in
(
block
);
bitset_clear_all
(
live
);
...
...
@@ -714,17 +545,17 @@ static void assign(ir_node *block, void *env_ptr)
void
be_ra_chordal_init
(
void
)
{
dbg
=
firm_dbg_register
(
DBG_
BERA
);
dbg
=
firm_dbg_register
(
DBG_
CHORDAL
);
firm_dbg_set_mask
(
dbg
,
0
);
}
void
be_ra_chordal
(
ir_graph
*
irg
,
be_chordal_env_t
*
be_ra_chordal
(
ir_graph
*
irg
,
const
arch_env_t
*
arch_env
,
const
arch_register_class_t
*
cls
)
{
int
node_count
=
get_graph_node_count
(
irg
);
int
colors_n
=
arch_register_class_n_regs
(
cls
);
env_t
*
env
=
malloc
(
sizeof
(
*
env
));
be_chordal_
env_t
*
env
=
malloc
(
sizeof
(
*
env
));
if
(
get_irg_dom_state
(
irg
)
!=
dom_consistent
)
compute_doms
(
irg
);
...
...
@@ -743,36 +574,39 @@ void be_ra_chordal(ir_graph *irg,
env
->
cls
=
cls
;
env
->
arch_env
=
arch_env
;
env
->
irg
=
irg
;
env
->
border_heads
=
pmap_create
();
/* First, determine the pressure */
dom_tree_walk_irg
(
irg
,
pressure
,
NULL
,
env
);
/* Insert probable spills */
be_ra_chordal_spill
(
irg
);
be_ra_chordal_spill
(
env
);
/* Assign the colors */
dom_tree_walk_irg
(
irg
,
assign
,
NULL
,
env
);
#ifdef DUMP_IFG
dump_ifg
(
env
);
#endif
#ifdef DUMP_INTERVALS
{
char
buf
[
128
];
plotter_t
*
plotter
;
ir_snprintf
(
buf
,
sizeof
(
buf
),
"ifg_%F.dot"
,
irg
);
dump_ifg
(
irg
,
env
->
edges
,
buf
);
}
#endif
ir_snprintf
(
buf
,
sizeof
(
buf
),
"ifg_%s_%F.eps"
,
cls
->
name
,
irg
);
plotter
=
new_plotter_ps
(
buf
);
#ifdef DUMP_INTERVALS
dump_intv_cfg
(
env
);
draw_interval_tree
(
&
draw_chordal_def_opts
,
env
,
plotter
,
arch_env
,
cls
);
plotter_free
(
plotter
);
}
#endif
set_irg_ra_link
(
irg
,
env
)
;
return
env
;
}
void
be_ra_chordal_done
(
ir_graph
*
irg
)
void
be_ra_chordal_done
(
be_chordal_env_t
*
env
)
{
env_t
*
env
=
get_irg_ra_link
(
irg
);
#ifdef BUILD_GRAPH
{
if_node_t
*
ifn
;
...
...
@@ -783,18 +617,14 @@ void be_ra_chordal_done(ir_graph *irg)
}
#endif
pmap_destroy
(
env
->
border_heads
);
obstack_free
(
&
env
->
obst
,
NULL
);
free
(
env
);
}
int
phi_op
s_interfere
(
const
ir_node
*
a
,
const
ir_node
*
b
)
int
node
s_interfere
(
const
be_chordal_env_t
*
env
,
const
ir_node
*
a
,
const
ir_node
*
b
)
{
#ifdef BUILD_GRAPH
ir_graph
*
irg
=
get_irn_irg
(
a
);
env_t
*
env
=
get_irg_ra_link
(
irg
);
assert
(
irg
==
get_irn_irg
(
b
)
&&
"Both nodes must be in the same graph"
);
return
are_connected
(
env
,
get_irn_graph_nr
(
a
),
get_irn_graph_nr
(
b
));
#else
return
values_interfere
(
a
,
b
);
...
...
@@ -802,19 +632,13 @@ int phi_ops_interfere(const ir_node *a, const ir_node *b)
}
#ifdef BUILD_GRAPH
/** TODO remove this
* Deprecated. Use be_ra_get_ifg_edges instead.
*/
set
*
be_ra_get_ifg
(
ir_graph
*
irg
)
{
return
((
env_t
*
)
get_irg_ra_link
(
irg
))
->
edges
;
}
set
*
be_ra_get_ifg_edges
(
ir_graph
*
irg
)
{
return
((
env
_t
*
)
get_irg_ra_link
(
irg
))
->
edges
;
set
*
be_ra_get_ifg_edges
(
const
be_chordal_env_t
*
env
)
{
return
env
->
edges
;
}
set
*
be_ra_get_ifg_nodes
(
ir_graph
*
irg
)
{
return
((
env
_t
*
)
get_irg_ra_link
(
irg
))
->
nodes
;
set
*
be_ra_get_ifg_nodes
(
const
be_chordal_env_t
*
env
)
{
return
env
->
nodes
;
}
#endif
ir/be/bechordal.h
View file @
165e48ff
...
...
@@ -13,12 +13,14 @@
#include
"bearch.h"
typedef
struct
_be_chordal_env_t
be_chordal_env_t
;
/**
* Allocate registers for an ir graph.
* @param irg The graph.
* @return Some internal data to be freed with be_ra_chordal_done().
*/
void
be_ra_chordal
(
ir_graph
*
irg
,
be_chordal_env_t
*
be_ra_chordal
(
ir_graph
*
irg
,
const
arch_env_t
*
arch_env
,
const
arch_register_class_t
*
cls
);
...
...
@@ -26,7 +28,7 @@ void be_ra_chordal(ir_graph *irg,
* Free data from the chordal register allocation.
* @param irg The graph.
*/
void
be_ra_chordal_done
(
ir_graph
*
irg
);
void
be_ra_chordal_done
(
be_chordal_env_t
*
info
);
/**
* Init some things for the chordal register allocator.
...
...
ir/be/bechordal_draw.c
0 → 100644
View file @
165e48ff
/**
* @file bechordal_draw.c
* @date 12.05.2005
* @author Sebastian Hack
*
* Paint chordal graphs.
*
* Copyright (C) 2005 Universitaet Karlsruhe
* Released under the GPL
*/
#include
<limits.h>
#include
"pmap.h"
#include
"irgwalk.h"
#include
"irprintf.h"
#include
"irouts.h"
#include
"belive_t.h"
#include
"bechordal_t.h"
#include
"besched_t.h"
#include
"bechordal_draw.h"
typedef
struct
{
be_chordal_env_t
*
env
;
plotter_t
inh
;
const
color_t
*
color
;
int
width
;
}
base_plotter_t
;
#define decl_self(type, from) \
type *self = (type *) from
static
void
set_color
(
plotter_t
*
_self
,
const
color_t
*
color
)
{
decl_self
(
base_plotter_t
,
_self
);
self
->
color
=
color
;
}
static
const
color_t
*
get_color
(
const
plotter_t
*
_self
)
{
decl_self
(
const
base_plotter_t
,
_self
);
return
self
->
color
;
}
static
void
set_width
(
plotter_t
*
_self
,
int
width
)
{
decl_self
(
base_plotter_t
,
_self
);
self
->
width
=
width
;
}
static
int
get_width
(
const
plotter_t
*
_self
)
{
decl_self
(
const
base_plotter_t
,
_self
);
return
self
->
width
;
}
static
void
plotter_default_free
(
plotter_t
*
self
)
{
}
typedef
struct
{
base_plotter_t
inh
;
const
char
*
filename
;
FILE
*
f
;
}
ps_plotter_t
;
static
void
ps_begin
(
plotter_t
*
_self
,
const
rect_t
*
vis
)
{
FILE
*
f
;
decl_self
(
ps_plotter_t
,
_self
);
f
=
self
->
f
=
fopen
(
self
->
filename
,
"wt"
);
fprintf
(
f
,
"%%!PS-Adobe-2.0
\n
"
);
fprintf
(
f
,
"%%%%BoundingBox: %d %d %d %d
\n
"
,
vis
->
x
,
vis
->
y
,
vis
->
w
,
vis
->
h
);
#if 0
fprintf(f, "/mainfont /Courier findfont %f scalefont def\n", 10.0);
fprintf(f, "mainfont setfont\n");
#endif
}
static
void
ps_setcolor
(
plotter_t
*
_self
,
const
color_t
*
color
)
{
decl_self
(
ps_plotter_t
,
_self
);
set_color
(
_self
,
color
);
fprintf
(
self
->
f
,
"%.2f %.2f %.2f setrgbcolor
\n
"
,
color
->
r
,
color
->
g
,
color
->
b
);
}
static
void
ps_line
(
plotter_t
*
_self
,
int
x1
,
int
y1
,
int
x2
,
int
y2
)
{
decl_self
(
ps_plotter_t
,
_self
);
fprintf
(
self
->
f
,
"%d %d moveto
\n
"
,
x1
,
y1
);
fprintf
(
self
->
f
,
"%d %d lineto
\n
"
,
x2
,
y2
);
fprintf
(
self
->
f
,
"stroke
\n
"
);
}
static
void
ps_box
(
plotter_t
*
_self
,
const
rect_t
*
rect
)
{
decl_self
(
ps_plotter_t
,
_self
);
fprintf
(
self
->
f
,
"%d %d %d %d rectstroke
\n
"
,
rect
->
x
,
rect
->
y
,
rect
->
w
,
rect
->
h
);
}
void
ps_text
(
plotter_t
*
_self
,
int
x
,
int
y
,
const
char
*
str
)
{
decl_self
(
ps_plotter_t
,
_self
);
fprintf
(
self
->
f
,
"%d %d moveto
\n
"
,
x
,
y
);
fprintf
(
self
->
f
,
"(%s) show
\n
"
,
str
);
}