Skip to content

Commit 78ab83e

Browse files
author
Daniel Kroening
authored
Merge pull request #399 from smowton/basic_jsr_implementation
Add basic jsr / ret implementation. Fixes #280
2 parents 43be05d + a492935 commit 78ab83e

File tree

6 files changed

+133
-2
lines changed

6 files changed

+133
-2
lines changed

regression/cbmc-java/jsr1/test.desc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
edu/emory/mathcs/backport/java/util/concurrent/ConcurrentHashMap.class
3+
--show-goto-functions
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
--
7+
\\"statement\\" (\\"jsr\\")
8+
\\"statement\\" (\\"ret\\")

regression/cbmc-java/jsr2/test.class

199 Bytes
Binary file not shown.

regression/cbmc-java/jsr2/test.desc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE
2+
test.class
3+
--show-goto-functions
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
Labels: pc3
7+
Labels: pc6
8+
Labels: pc9
9+
Labels: pc12
10+
Labels: pc13
11+
--
12+
\\"statement\\" (\\"jsr\\")
13+
\\"statement\\" (\\"ret\\")

regression/cbmc-java/jsr2/test.j

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
; This is Jasmin assembler syntax-- see http://jasmin.sourceforge.net/guide.html or apt-get install jasmin-sable
2+
3+
.class public test
4+
.super java/lang/Object
5+
6+
; standard initializer
7+
.method public <init>()V
8+
aload_0
9+
invokenonvirtual java/lang/Object/<init>()V
10+
return
11+
.end method
12+
13+
.method public static main()V
14+
.limit locals 4
15+
.limit stack 1
16+
goto runsrs
17+
sr1:
18+
astore_0
19+
ret 0
20+
runsrs:
21+
jsr sr1
22+
jsr sr2
23+
return
24+
sr2:
25+
astore_3
26+
ret 3
27+
.end method
28+

src/java_bytecode/java_bytecode_convert_method.cpp

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,9 @@ codet java_bytecode_convert_methodt::convert_instructions(
544544
address_mapt address_map;
545545
std::set<unsigned> targets;
546546

547+
std::vector<unsigned> jsr_ret_targets;
548+
std::vector<instructionst::const_iterator> ret_instructions;
549+
547550
for(instructionst::const_iterator
548551
i_it=instructions.begin();
549552
i_it!=instructions.end();
@@ -586,6 +589,15 @@ codet java_bytecode_convert_methodt::convert_instructions(
586589
targets.insert(target);
587590

588591
a_entry.first->second.successors.push_back(target);
592+
593+
if(i_it->statement=="jsr" ||
594+
i_it->statement=="jsr_w")
595+
{
596+
instructionst::const_iterator next=i_it;
597+
assert(++next!=instructions.end() && "jsr without valid return address?");
598+
targets.insert(next->address);
599+
jsr_ret_targets.push_back(next->address);
600+
}
589601
}
590602
else if(i_it->statement=="tableswitch" ||
591603
i_it->statement=="lookupswitch")
@@ -603,6 +615,23 @@ codet java_bytecode_convert_methodt::convert_instructions(
603615
is_label=!is_label;
604616
}
605617
}
618+
else if(i_it->statement=="ret")
619+
{
620+
// Finish these later, once we've seen all jsr instructions.
621+
ret_instructions.push_back(i_it);
622+
}
623+
}
624+
625+
// Draw edges from every `ret` to every `jsr` successor.
626+
// Could do better with flow analysis to distinguish multiple subroutines within
627+
// the same function.
628+
for(const auto retinst : ret_instructions)
629+
{
630+
auto& a_entry=address_map.at(retinst->address);
631+
a_entry.successors.insert(
632+
a_entry.successors.end(),
633+
jsr_ret_targets.begin(),
634+
jsr_ret_targets.end());
606635
}
607636

608637
for(const auto &address : address_map)
@@ -921,6 +950,48 @@ codet java_bytecode_convert_methodt::convert_instructions(
921950
code_gotot code_goto(label(number));
922951
c=code_goto;
923952
}
953+
else if(statement=="jsr" || statement=="jsr_w")
954+
{
955+
// As 'goto', except we must also push the subroutine return address:
956+
assert(op.empty() && results.size()==1);
957+
irep_idt number=to_constant_expr(arg0).get_value();
958+
code_gotot code_goto(label(number));
959+
c=code_goto;
960+
results[0]=as_number(
961+
std::next(i_it)->address,
962+
pointer_typet(void_typet(), 64));
963+
}
964+
else if(statement=="ret")
965+
{
966+
// Since we have a bounded target set, make life easier on our analyses
967+
// and write something like:
968+
// if(retaddr==5) goto 5; else if(retaddr==10) goto 10; ...
969+
assert(op.empty() && results.empty());
970+
code_blockt branches;
971+
auto retvar=variable(arg0, 'a', i_it->address, INST_INDEX, NO_CAST);
972+
assert(!jsr_ret_targets.empty());
973+
for(size_t idx=0, idxlim=jsr_ret_targets.size(); idx!=idxlim; ++idx)
974+
{
975+
irep_idt number=std::to_string(jsr_ret_targets[idx]);
976+
code_gotot g(label(number));
977+
g.add_source_location()=i_it->source_location;
978+
if(idx==idxlim-1)
979+
branches.move_to_operands(g);
980+
else
981+
{
982+
code_ifthenelset branch;
983+
auto address_ptr=as_number(
984+
jsr_ret_targets[idx],
985+
pointer_typet(void_typet(), 64));
986+
branch.cond()=equal_exprt(retvar, address_ptr);
987+
branch.cond().add_source_location()=i_it->source_location;
988+
branch.then_case()=g;
989+
branch.add_source_location()=i_it->source_location;
990+
branches.move_to_operands(branch);
991+
}
992+
}
993+
c=std::move(branches);
994+
}
924995
else if(statement=="iconst_m1")
925996
{
926997
assert(results.size()==1);
@@ -1521,8 +1592,19 @@ codet java_bytecode_convert_methodt::convert_instructions(
15211592
else
15221593
{
15231594
c.make_block();
1524-
forall_operands(o_it, more_code)
1525-
c.copy_to_operands(*o_it);
1595+
auto& last_statement=to_code_block(c).find_last_statement();
1596+
if(last_statement.get_statement()==ID_goto)
1597+
{
1598+
// Insert stack twiddling before branch:
1599+
last_statement.make_block();
1600+
last_statement.operands().insert(
1601+
last_statement.operands().begin(),
1602+
more_code.operands().begin(),
1603+
more_code.operands().end());
1604+
}
1605+
else
1606+
forall_operands(o_it, more_code)
1607+
c.copy_to_operands(*o_it);
15261608
}
15271609
}
15281610

0 commit comments

Comments
 (0)