Module Deadcode
Elimination of unneeded computations over RTL.
Require
Import
Coqlib
Maps
Errors
Integers
Floats
Lattice
Kildall
.
Require
Import
AST
Linking
Builtins
.
Require
Import
Memory
Registers
Op
RTL
.
Require
Import
ValueDomain
ValueAnalysis
NeedDomain
NeedOp
.
Part 1: the static analysis
Definition
add_need_all
(
r
:
reg
) (
ne
:
nenv
) :
nenv
:=
NE.set
r
All
ne
.
Definition
add_need
(
r
:
reg
) (
nv
:
nval
) (
ne
:
nenv
) :
nenv
:=
NE.set
r
(
nlub
nv
(
NE.get
r
ne
))
ne
.
Fixpoint
add_needs_all
(
rl
:
list
reg
) (
ne
:
nenv
) :
nenv
:=
match
rl
with
|
nil
=>
ne
|
r1
::
rs
=>
add_need_all
r1
(
add_needs_all
rs
ne
)
end
.
Fixpoint
add_needs
(
rl
:
list
reg
) (
nvl
:
list
nval
) (
ne
:
nenv
) :
nenv
:=
match
rl
,
nvl
with
|
nil
, _ =>
ne
|
r1
::
rs
,
nil
=>
add_needs_all
rl
ne
|
r1
::
rs
,
nv1
::
nvs
=>
add_need
r1
nv1
(
add_needs
rs
nvs
ne
)
end
.
Definition
add_ros_need_all
(
ros
:
reg
+
ident
) (
ne
:
nenv
) :
nenv
:=
match
ros
with
|
inl
r
=>
add_need_all
r
ne
|
inr
s
=>
ne
end
.
Definition
add_opt_need_all
(
or
:
option
reg
) (
ne
:
nenv
) :
nenv
:=
match
or
with
|
Some
r
=>
add_need_all
r
ne
|
None
=>
ne
end
.
Definition
kill
(
r
:
reg
) (
ne
:
nenv
) :
nenv
:=
NE.set
r
Nothing
ne
.
Definition
is_dead
(
v
:
nval
) :=
match
v
with
Nothing
=>
true
| _ =>
false
end
.
Definition
is_int_zero
(
v
:
nval
) :=
match
v
with
I
n
=>
Int.eq
n
Int.zero
| _ =>
false
end
.
Fixpoint
transfer_builtin_arg
(
nv
:
nval
) (
na
:
NA.t
) (
a
:
builtin_arg
reg
) :
NA.t
:=
let
(
ne
,
nm
) :=
na
in
match
a
with
|
BA
r
=> (
add_need
r
nv
ne
,
nm
)
|
BA_int
_ |
BA_long
_ |
BA_float
_ |
BA_single
_
|
BA_addrstack
_ |
BA_addrglobal
_ _ => (
ne
,
nm
)
|
BA_loadstack
chunk
ofs
=> (
ne
,
nmem_add
nm
(
Stk
ofs
) (
size_chunk
chunk
))
|
BA_loadglobal
chunk
id
ofs
=> (
ne
,
nmem_add
nm
(
Gl
id
ofs
) (
size_chunk
chunk
))
|
BA_splitlong
hi
lo
=>
transfer_builtin_arg
All
(
transfer_builtin_arg
All
na
hi
)
lo
|
BA_addptr
hi
lo
=>
transfer_builtin_arg
All
(
transfer_builtin_arg
All
na
hi
)
lo
end
.
Definition
transfer_builtin_args
(
na
:
NA.t
) (
al
:
list
(
builtin_arg
reg
)) :
NA.t
:=
List.fold_left
(
transfer_builtin_arg
All
)
al
na
.
Definition
kill_builtin_res
(
res
:
builtin_res
reg
) (
ne
:
NE.t
) :
NE.t
:=
match
res
with
|
BR
r
=>
kill
r
ne
| _ =>
ne
end
.
Definition
builtin_res_dead
(
res
:
builtin_res
reg
) (
ne
:
NE.t
) :=
match
res
with
|
BR
r
=>
is_dead
(
nreg
ne
r
)
| _ =>
false
end
.
Function
transfer_builtin
(
app
:
VA.t
) (
ef
:
external_function
)
(
args
:
list
(
builtin_arg
reg
)) (
res
:
builtin_res
reg
)
(
ne
:
NE.t
) (
nm
:
nmem
) :
NA.t
:=
match
ef
,
args
with
|
EF_vload
chunk
,
a1
::
nil
=>
transfer_builtin_arg
All
(
kill_builtin_res
res
ne
,
nmem_add
nm
(
aaddr_arg
app
a1
) (
size_chunk
chunk
))
a1
|
EF_vstore
chunk
,
a1
::
a2
::
nil
=>
transfer_builtin_arg
All
(
transfer_builtin_arg
(
store_argument
chunk
)
(
kill_builtin_res
res
ne
,
nm
)
a2
)
a1
|
EF_memcpy
sz
al
,
dst
::
src
::
nil
=>
if
nmem_contains
nm
(
aaddr_arg
app
dst
)
sz
then
transfer_builtin_args
(
kill_builtin_res
res
ne
,
nmem_add
(
nmem_remove
nm
(
aaddr_arg
app
dst
)
sz
) (
aaddr_arg
app
src
)
sz
)
args
else
(
ne
,
nm
)
| (
EF_annot
_ _ _ |
EF_annot_val
_ _ _), _ =>
transfer_builtin_args
(
kill_builtin_res
res
ne
,
nm
)
args
|
EF_debug
_ _ _, _ =>
(
kill_builtin_res
res
ne
,
nm
)
|
EF_builtin
name
sg
,
args
=>
match
lookup_builtin_function
name
sg
with
|
Some
bf
=>
if
builtin_res_dead
res
ne
then
(
ne
,
nm
)
else
transfer_builtin_args
(
kill_builtin_res
res
ne
,
nm
)
args
| _ =>
transfer_builtin_args
(
kill_builtin_res
res
ne
,
nmem_all
)
args
end
| _, _ =>
transfer_builtin_args
(
kill_builtin_res
res
ne
,
nmem_all
)
args
end
.
Definition
transfer
(
f
:
function
) (
dm
:
defmap
) (
approx
:
PMap.t
VA.t
)
(
pc
:
node
) (
after
:
NA.t
) :
NA.t
:=
let
(
ne
,
nm
) :=
after
in
match
f
.(
fn_code
)!
pc
with
|
None
=>
NA.bot
|
Some
(
Inop
s
) =>
after
|
Some
(
Iop
op
args
res
s
) =>
let
nres
:=
nreg
ne
res
in
if
is_dead
nres
then
after
else
if
is_int_zero
nres
then
(
kill
res
ne
,
nm
)
else
(
add_needs
args
(
needs_of_operation
op
nres
) (
kill
res
ne
),
nm
)
|
Some
(
Iload
chunk
addr
args
dst
s
) =>
let
ndst
:=
nreg
ne
dst
in
if
is_dead
ndst
then
after
else
if
is_int_zero
ndst
then
(
kill
dst
ne
,
nm
)
else
(
add_needs_all
args
(
kill
dst
ne
),
nmem_add
nm
(
aaddressing
approx
!!
pc
addr
args
) (
size_chunk
chunk
))
|
Some
(
Istore
chunk
addr
args
src
s
) =>
let
p
:=
aaddressing
approx
!!
pc
addr
args
in
if
nmem_contains
nm
p
(
size_chunk
chunk
)
then
(
add_needs_all
args
(
add_need
src
(
store_argument
chunk
)
ne
),
nmem_remove
nm
p
(
size_chunk
chunk
))
else
after
|
Some
(
Icall
sig
ros
args
res
s
) =>
match
is_known_runtime_function
dm
ros
with
|
None
=>
(
add_needs_all
args
(
add_ros_need_all
ros
(
kill
res
ne
)),
nmem_all
)
|
Some
bf
=>
let
nres
:=
nreg
ne
res
in
if
is_dead
nres
then
after
else
(
add_needs_all
args
(
add_ros_need_all
ros
(
kill
res
ne
)),
nm
)
end
|
Some
(
Itailcall
sig
ros
args
) =>
(
add_needs_all
args
(
add_ros_need_all
ros
NE.bot
),
nmem_dead_stack
f
.(
fn_stacksize
))
|
Some
(
Ibuiltin
ef
args
res
s
) =>
transfer_builtin
approx
!!
pc
ef
args
res
ne
nm
|
Some
(
Icond
cond
args
s1
s2
) =>
if
peq
s1
s2
then
after
else
(
add_needs
args
(
needs_of_condition
cond
)
ne
,
nm
)
|
Some
(
Ijumptable
arg
tbl
) =>
(
add_need_all
arg
ne
,
nm
)
|
Some
(
Ireturn
optarg
) =>
(
add_opt_need_all
optarg
ne
,
nmem_dead_stack
f
.(
fn_stacksize
))
end
.
Module
DS
:=
Backward_Dataflow_Solver
(
NA
)(
NodeSetBackward
).
Definition
analyze
(
f
:
function
) (
dm
:
defmap
) (
approx
:
PMap.t
VA.t
):
option
(
PMap.t
NA.t
) :=
DS.fixpoint
f
.(
fn_code
)
successors_instr
(
transfer
f
dm
approx
).
Part 2: the code transformation
Definition
transf_instr
(
dm
:
defmap
) (
approx
:
PMap.t
VA.t
) (
an
:
PMap.t
NA.t
)
(
pc
:
node
) (
instr
:
instruction
) :=
match
instr
with
|
Iop
op
args
res
s
=>
let
nres
:=
nreg
(
fst
an
!!
pc
)
res
in
if
is_dead
nres
then
Inop
s
else
if
is_int_zero
nres
then
Iop
(
Ointconst
Int.zero
)
nil
res
s
else
if
operation_is_redundant
op
nres
then
match
args
with
|
arg
:: _ =>
Iop
Omove
(
arg
::
nil
)
res
s
|
nil
=>
instr
end
else
instr
|
Iload
chunk
addr
args
dst
s
=>
let
ndst
:=
nreg
(
fst
an
!!
pc
)
dst
in
if
is_dead
ndst
then
Inop
s
else
if
is_int_zero
ndst
then
Iop
(
Ointconst
Int.zero
)
nil
dst
s
else
instr
|
Istore
chunk
addr
args
src
s
=>
let
p
:=
aaddressing
approx
!!
pc
addr
args
in
if
nmem_contains
(
snd
an
!!
pc
)
p
(
size_chunk
chunk
)
then
instr
else
Inop
s
|
Icall
sig
ros
args
res
s
=>
match
is_known_runtime_function
dm
ros
with
|
None
=>
instr
|
Some
bf
=>
let
nres
:=
nreg
(
fst
an
!!
pc
)
res
in
if
is_dead
nres
then
Inop
s
else
instr
end
|
Ibuiltin
(
EF_memcpy
sz
al
) (
dst
::
src
::
nil
)
res
s
=>
if
nmem_contains
(
snd
an
!!
pc
) (
aaddr_arg
approx
!!
pc
dst
)
sz
then
instr
else
Inop
s
|
Ibuiltin
(
EF_builtin
name
sg
)
args
res
s
=>
match
lookup_builtin_function
name
sg
,
builtin_res_dead
res
(
fst
an
!!
pc
)
with
|
Some
bf
,
true
=>
Inop
s
| _, _ =>
instr
end
|
Icond
cond
args
s1
s2
=>
if
peq
s1
s2
then
Inop
s1
else
instr
| _ =>
instr
end
.
Definition
transf_function
(
dm
:
defmap
) (
rm
:
romem
) (
f
:
function
) :
res
function
:=
let
approx
:=
ValueAnalysis.analyze
rm
f
in
match
analyze
f
dm
approx
with
|
Some
an
=>
OK
{|
fn_sig
:=
f
.(
fn_sig
);
fn_params
:=
f
.(
fn_params
);
fn_stacksize
:=
f
.(
fn_stacksize
);
fn_code
:=
PTree.map
(
transf_instr
dm
approx
an
)
f
.(
fn_code
);
fn_entrypoint
:=
f
.(
fn_entrypoint
) |}
|
None
=>
Error
(
msg
"Neededness analysis failed"
)
end
.
Definition
transf_fundef
(
dm
:
defmap
) (
rm
:
romem
) (
fd
:
fundef
) :
res
fundef
:=
AST.transf_partial_fundef
(
transf_function
dm
rm
)
fd
.
Definition
transf_program
(
p
:
program
) :
res
program
:=
transform_partial_program
(
transf_fundef
(
prog_defmap
p
) (
romem_for
p
))
p
.
Generated by
coq2html
AltStyle
によって変換されたページ
(->オリジナル)
/
アドレス:
モード:
デフォルト
音声ブラウザ
ルビ付き
配色反転
文字拡大
モバイル