You can subscribe to this list here.
| 2002 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
(1) |
Oct
(122) |
Nov
(152) |
Dec
(69) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2003 |
Jan
(6) |
Feb
(25) |
Mar
(73) |
Apr
(82) |
May
(24) |
Jun
(25) |
Jul
(10) |
Aug
(11) |
Sep
(10) |
Oct
(54) |
Nov
(203) |
Dec
(182) |
| 2004 |
Jan
(307) |
Feb
(305) |
Mar
(430) |
Apr
(312) |
May
(187) |
Jun
(342) |
Jul
(487) |
Aug
(637) |
Sep
(336) |
Oct
(373) |
Nov
(441) |
Dec
(210) |
| 2005 |
Jan
(385) |
Feb
(480) |
Mar
(636) |
Apr
(544) |
May
(679) |
Jun
(625) |
Jul
(810) |
Aug
(838) |
Sep
(634) |
Oct
(521) |
Nov
(965) |
Dec
(543) |
| 2006 |
Jan
(494) |
Feb
(431) |
Mar
(546) |
Apr
(411) |
May
(406) |
Jun
(322) |
Jul
(256) |
Aug
(401) |
Sep
(345) |
Oct
(542) |
Nov
(308) |
Dec
(481) |
| 2007 |
Jan
(427) |
Feb
(326) |
Mar
(367) |
Apr
(255) |
May
(244) |
Jun
(204) |
Jul
(223) |
Aug
(231) |
Sep
(354) |
Oct
(374) |
Nov
(497) |
Dec
(362) |
| 2008 |
Jan
(322) |
Feb
(482) |
Mar
(658) |
Apr
(422) |
May
(476) |
Jun
(396) |
Jul
(455) |
Aug
(267) |
Sep
(280) |
Oct
(253) |
Nov
(232) |
Dec
(304) |
| 2009 |
Jan
(486) |
Feb
(470) |
Mar
(458) |
Apr
(423) |
May
(696) |
Jun
(461) |
Jul
(551) |
Aug
(575) |
Sep
(134) |
Oct
(110) |
Nov
(157) |
Dec
(102) |
| 2010 |
Jan
(226) |
Feb
(86) |
Mar
(147) |
Apr
(117) |
May
(107) |
Jun
(203) |
Jul
(193) |
Aug
(238) |
Sep
(300) |
Oct
(246) |
Nov
(23) |
Dec
(75) |
| 2011 |
Jan
(133) |
Feb
(195) |
Mar
(315) |
Apr
(200) |
May
(267) |
Jun
(293) |
Jul
(353) |
Aug
(237) |
Sep
(278) |
Oct
(611) |
Nov
(274) |
Dec
(260) |
| 2012 |
Jan
(303) |
Feb
(391) |
Mar
(417) |
Apr
(441) |
May
(488) |
Jun
(655) |
Jul
(590) |
Aug
(610) |
Sep
(526) |
Oct
(478) |
Nov
(359) |
Dec
(372) |
| 2013 |
Jan
(467) |
Feb
(226) |
Mar
(391) |
Apr
(281) |
May
(299) |
Jun
(252) |
Jul
(311) |
Aug
(352) |
Sep
(481) |
Oct
(571) |
Nov
(222) |
Dec
(231) |
| 2014 |
Jan
(185) |
Feb
(329) |
Mar
(245) |
Apr
(238) |
May
(281) |
Jun
(399) |
Jul
(382) |
Aug
(500) |
Sep
(579) |
Oct
(435) |
Nov
(487) |
Dec
(256) |
| 2015 |
Jan
(338) |
Feb
(357) |
Mar
(330) |
Apr
(294) |
May
(191) |
Jun
(108) |
Jul
(142) |
Aug
(261) |
Sep
(190) |
Oct
(54) |
Nov
(83) |
Dec
(22) |
| 2016 |
Jan
(49) |
Feb
(89) |
Mar
(33) |
Apr
(50) |
May
(27) |
Jun
(34) |
Jul
(53) |
Aug
(53) |
Sep
(98) |
Oct
(206) |
Nov
(93) |
Dec
(53) |
| 2017 |
Jan
(65) |
Feb
(82) |
Mar
(102) |
Apr
(86) |
May
(187) |
Jun
(67) |
Jul
(23) |
Aug
(93) |
Sep
(65) |
Oct
(45) |
Nov
(35) |
Dec
(17) |
| 2018 |
Jan
(26) |
Feb
(35) |
Mar
(38) |
Apr
(32) |
May
(8) |
Jun
(43) |
Jul
(27) |
Aug
(30) |
Sep
(43) |
Oct
(42) |
Nov
(38) |
Dec
(67) |
| 2019 |
Jan
(32) |
Feb
(37) |
Mar
(53) |
Apr
(64) |
May
(49) |
Jun
(18) |
Jul
(14) |
Aug
(53) |
Sep
(25) |
Oct
(30) |
Nov
(49) |
Dec
(31) |
| 2020 |
Jan
(87) |
Feb
(45) |
Mar
(37) |
Apr
(51) |
May
(99) |
Jun
(36) |
Jul
(11) |
Aug
(14) |
Sep
(20) |
Oct
(24) |
Nov
(40) |
Dec
(23) |
| 2021 |
Jan
(14) |
Feb
(53) |
Mar
(85) |
Apr
(15) |
May
(19) |
Jun
(3) |
Jul
(14) |
Aug
(1) |
Sep
(57) |
Oct
(73) |
Nov
(56) |
Dec
(22) |
| 2022 |
Jan
(3) |
Feb
(22) |
Mar
(6) |
Apr
(55) |
May
(46) |
Jun
(39) |
Jul
(15) |
Aug
(9) |
Sep
(11) |
Oct
(34) |
Nov
(20) |
Dec
(36) |
| 2023 |
Jan
(79) |
Feb
(41) |
Mar
(99) |
Apr
(169) |
May
(48) |
Jun
(16) |
Jul
(16) |
Aug
(57) |
Sep
(19) |
Oct
|
Nov
|
Dec
|
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
|
|
|
1
(1) |
2
(2) |
3
(2) |
4
(3) |
5
(1) |
|
6
(2) |
7
|
8
|
9
|
10
(1) |
11
|
12
|
|
13
|
14
(2) |
15
(27) |
16
(1) |
17
(4) |
18
(4) |
19
|
|
20
|
21
(1) |
22
(2) |
23
|
24
(2) |
25
|
26
(2) |
|
27
|
28
(22) |
29
(5) |
30
(3) |
31
(6) |
|
|
|
From: Ivo R. <ir...@so...> - 2017-08-28 22:26:59
|
https://sourceware.org/git/gitweb.cgi?p=valgrind.git;h=3e5b6ae887f419c74a968b320e263edfd72f0890 commit 3e5b6ae887f419c74a968b320e263edfd72f0890 Author: Ivo Raisr <iv...@iv...> Date: Tue Aug 8 06:40:24 2017 +0200 Introduce an important assertion for a claim from libvex_ir.h. Diff: --- VEX/priv/main_util.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/VEX/priv/main_util.c b/VEX/priv/main_util.c index e9a496b..4ed8866 100644 --- a/VEX/priv/main_util.c +++ b/VEX/priv/main_util.c @@ -123,6 +123,8 @@ void vexAllocSanityCheck ( void ) vassert(IS_WORD_ALIGNED(private_LibVEX_alloc_curr)); vassert(IS_WORD_ALIGNED(private_LibVEX_alloc_last+1)); # undef IS_WORD_ALIGNED + + STATIC_ASSERT(sizeof(IRTemp) == 4); } /* The current allocation mode. */ |
|
From: Ivo R. <ir...@so...> - 2017-08-28 22:26:54
|
https://sourceware.org/git/gitweb.cgi?p=valgrind.git;h=53d73de7be7713619fcc38a1538fa7cebcf252dc commit 53d73de7be7713619fcc38a1538fa7cebcf252dc Author: Ivo Raisr <iv...@iv...> Date: Tue Aug 8 10:13:43 2017 +0200 Make guest_x86_toIR.c compile under new rules. Diff: --- VEX/priv/guest_x86_toIR.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/VEX/priv/guest_x86_toIR.c b/VEX/priv/guest_x86_toIR.c index 9f6a41a..0a38531 100644 --- a/VEX/priv/guest_x86_toIR.c +++ b/VEX/priv/guest_x86_toIR.c @@ -208,7 +208,8 @@ static Addr32 guest_EIP_bbstart; translated. */ static Addr32 guest_EIP_curr_instr; -/* The IRSB* into which we're generating code. */ +/* The IRSB* into which we're generating code. All functions below work + implicitly with the main statement vector held by irsb->stmts. */ static IRSB* irsb; @@ -309,17 +310,18 @@ static IRSB* irsb; #define R_GS 5 -/* Add a statement to the list held by "irbb". */ +/* Add a statement to the main statement vector held by "irbb->stmts". */ static void stmt ( IRStmt* st ) { - addStmtToIRSB( irsb, st ); + addStmtToIRStmtVec(irsb->stmts, st); } -/* Generate a new temporary of the given type. */ +/* Generate a new temporary of the given type. + Works only for the main IRStmtVec #0. */ static IRTemp newTemp ( IRType ty ) { vassert(isPlausibleIRType(ty)); - return newIRTemp( irsb->tyenv, ty ); + return newIRTemp(irsb->tyenv, irsb->stmts, ty); } /* Various simple conversions */ @@ -970,7 +972,7 @@ static void setFlags_DEP1_DEP2_shift ( IROp op32, Int ccOp = ty==Ity_I8 ? 2 : (ty==Ity_I16 ? 1 : 0); vassert(ty == Ity_I8 || ty == Ity_I16 || ty == Ity_I32); - vassert(guard); + vassert(guard != IRTemp_INVALID); /* Both kinds of right shifts are handled by the same thunk operation. */ @@ -1728,7 +1730,6 @@ IRTemp disAMode ( Int* len, UChar sorb, Int delta, HChar* buf ) default: vpanic("disAMode(x86)"); - return 0; /*notreached*/ } } @@ -3432,7 +3433,6 @@ static IRTemp gen_LZCNT ( IRType ty, IRTemp src ) mkU32(8 * sizeofIRType(ty)), unop(Iop_Clz32, mkexpr(src32x)) )); - IRTemp res = newTemp(ty); assign(res, narrowTo(ty, mkexpr(res32))); return res; @@ -8050,8 +8050,6 @@ static IRTemp math_BSWAP ( IRTemp t1, IRType ty ) return t2; } vassert(0); - /*NOTREACHED*/ - return IRTemp_INVALID; } /*------------------------------------------------------------*/ @@ -15501,13 +15499,13 @@ DisResult disInstr_X86 ( IRSB* irsb_IN, guest_EIP_curr_instr = (Addr32)guest_IP; guest_EIP_bbstart = (Addr32)toUInt(guest_IP - delta); - x1 = irsb_IN->stmts_used; + x1 = irsb_IN->stmts->stmts_used; expect_CAS = False; dres = disInstr_X86_WRK ( &expect_CAS, resteerOkFn, resteerCisOk, callback_opaque, delta, archinfo, abiinfo, sigill_diag_IN ); - x2 = irsb_IN->stmts_used; + x2 = irsb_IN->stmts->stmts_used; vassert(x2 >= x1); /* See comment at the top of disInstr_X86_WRK for meaning of @@ -15515,7 +15513,7 @@ DisResult disInstr_X86 ( IRSB* irsb_IN, IRCAS as directed by the returned expect_CAS value. */ has_CAS = False; for (i = x1; i < x2; i++) { - if (irsb_IN->stmts[i]->tag == Ist_CAS) + if (irsb_IN->stmts->stmts[i]->tag == Ist_CAS) has_CAS = True; } @@ -15528,8 +15526,7 @@ DisResult disInstr_X86 ( IRSB* irsb_IN, callback_opaque, delta, archinfo, abiinfo, sigill_diag_IN ); for (i = x1; i < x2; i++) { - vex_printf("\t\t"); - ppIRStmt(irsb_IN->stmts[i]); + ppIRStmt(irsb_IN->stmts->stmts[i], irsb_IN->tyenv, 4); vex_printf("\n"); } /* Failure of this assertion is serious and denotes a bug in |
|
From: Ivo R. <ir...@so...> - 2017-08-28 22:26:49
|
https://sourceware.org/git/gitweb.cgi?p=valgrind.git;h=1112ed45eb9a2c1d210f00232e37603e587bb89a commit 1112ed45eb9a2c1d210f00232e37603e587bb89a Author: Ivo Raisr <iv...@iv...> Date: Tue Aug 8 09:50:19 2017 +0200 Make guest_generic_bb_to_IR.c compile under new rules. Diff: --- VEX/priv/guest_generic_bb_to_IR.c | 57 +++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/VEX/priv/guest_generic_bb_to_IR.c b/VEX/priv/guest_generic_bb_to_IR.c index 6df594d..64f324b 100644 --- a/VEX/priv/guest_generic_bb_to_IR.c +++ b/VEX/priv/guest_generic_bb_to_IR.c @@ -271,9 +271,9 @@ IRSB* bb_to_IR ( each). We won't know until later the extents and checksums of the areas, if any, that need to be checked. */ nop = IRStmt_NoOp(); - selfcheck_idx = irsb->stmts_used; + selfcheck_idx = irsb->stmts->stmts_used; for (i = 0; i < 3 * 5; i++) - addStmtToIRSB( irsb, nop ); + addStmtToIRStmtVec(irsb->stmts, nop); /* If the caller supplied a function to add its own preamble, use it now. */ @@ -322,7 +322,7 @@ IRSB* bb_to_IR ( /* This is the irsb statement array index of the first stmt in this insn. That will always be the instruction-mark descriptor. */ - first_stmt_idx = irsb->stmts_used; + first_stmt_idx = irsb->stmts->stmts_used; /* Add an instruction-mark statement. We won't know until after disassembling the instruction how long it instruction is, so @@ -341,19 +341,19 @@ IRSB* bb_to_IR ( libvex_guest_arm.h. */ if (arch_guest == VexArchARM && (guest_IP_curr_instr & 1)) { /* Thumb insn => mask out the T bit, but put it in delta */ - addStmtToIRSB( irsb, - IRStmt_IMark(guest_IP_curr_instr & ~(Addr)1, - 0, /* len */ - 1 /* delta */ - ) + addStmtToIRStmtVec(irsb->stmts, + IRStmt_IMark(guest_IP_curr_instr & ~(Addr)1, + 0, /* len */ + 1 /* delta */ + ) ); } else { /* All other targets: store IP as-is, and set delta to zero. */ - addStmtToIRSB( irsb, - IRStmt_IMark(guest_IP_curr_instr, - 0, /* len */ - 0 /* delta */ - ) + addStmtToIRStmtVec(irsb->stmts, + IRStmt_IMark(guest_IP_curr_instr, + 0, /* len */ + 0 /* delta */ + ) ); } @@ -408,18 +408,17 @@ IRSB* bb_to_IR ( } /* Fill in the insn-mark length field. */ - vassert(first_stmt_idx >= 0 && first_stmt_idx < irsb->stmts_used); - imark = irsb->stmts[first_stmt_idx]; + vassert(first_stmt_idx >= 0 && first_stmt_idx < irsb->stmts->stmts_used); + imark = irsb->stmts->stmts[first_stmt_idx]; vassert(imark); vassert(imark->tag == Ist_IMark); vassert(imark->Ist.IMark.len == 0); imark->Ist.IMark.len = dres.len; /* Print the resulting IR, if needed. */ - if (vex_traceflags & VEX_TRACE_FE) { - for (i = first_stmt_idx; i < irsb->stmts_used; i++) { - vex_printf(" "); - ppIRStmt(irsb->stmts[i]); + if (debug_print) { + for (i = first_stmt_idx; i < irsb->stmts->stmts_used; i++) { + ppIRStmt(irsb->stmts->stmts[i], irsb->tyenv, 3); vex_printf("\n"); } } @@ -432,9 +431,9 @@ IRSB* bb_to_IR ( /* Individual insn disassembly must finish the IR for each instruction with an assignment to the guest PC. */ - vassert(first_stmt_idx < irsb->stmts_used); + vassert(first_stmt_idx < irsb->stmts->stmts_used); /* it follows that irsb->stmts_used must be > 0 */ - { IRStmt* st = irsb->stmts[irsb->stmts_used-1]; + { IRStmt* st = irsb->stmts->stmts[irsb->stmts->stmts_used-1]; vassert(st); vassert(st->tag == Ist_Put); vassert(st->Ist.Put.offset == offB_GUEST_IP); @@ -693,8 +692,8 @@ IRSB* bb_to_IR ( the area of guest code to invalidate should we exit with a self-check failure. */ - tistart_tmp = newIRTemp(irsb->tyenv, guest_word_type); - tilen_tmp = newIRTemp(irsb->tyenv, guest_word_type); + tistart_tmp = newIRTemp(irsb->tyenv, irsb->stmts, guest_word_type); + tilen_tmp = newIRTemp(irsb->tyenv, irsb->stmts, guest_word_type); IRConst* base2check_IRConst = guest_word_type==Ity_I32 ? IRConst_U32(toUInt(base2check)) @@ -703,16 +702,16 @@ IRSB* bb_to_IR ( = guest_word_type==Ity_I32 ? IRConst_U32(len2check) : IRConst_U64(len2check); - irsb->stmts[selfcheck_idx + i * 5 + 0] + irsb->stmts->stmts[selfcheck_idx + i * 5 + 0] = IRStmt_WrTmp(tistart_tmp, IRExpr_Const(base2check_IRConst) ); - irsb->stmts[selfcheck_idx + i * 5 + 1] + irsb->stmts->stmts[selfcheck_idx + i * 5 + 1] = IRStmt_WrTmp(tilen_tmp, IRExpr_Const(len2check_IRConst) ); - irsb->stmts[selfcheck_idx + i * 5 + 2] + irsb->stmts->stmts[selfcheck_idx + i * 5 + 2] = IRStmt_Put( offB_GUEST_CMSTART, IRExpr_RdTmp(tistart_tmp) ); - irsb->stmts[selfcheck_idx + i * 5 + 3] + irsb->stmts->stmts[selfcheck_idx + i * 5 + 3] = IRStmt_Put( offB_GUEST_CMLEN, IRExpr_RdTmp(tilen_tmp) ); /* Generate the entry point descriptors */ @@ -754,7 +753,7 @@ IRSB* bb_to_IR ( ); } - irsb->stmts[selfcheck_idx + i * 5 + 4] + irsb->stmts->stmts[selfcheck_idx + i * 5 + 4] = IRStmt_Exit( IRExpr_Binop( host_word_type==Ity_I64 ? Iop_CmpNE64 : Iop_CmpNE32, @@ -777,7 +776,7 @@ IRSB* bb_to_IR ( Print it if necessary.*/ vassert(irsb->next != NULL); if (debug_print) { - vex_printf(" "); + vex_printf(" "); vex_printf( "PUT(%d) = ", irsb->offsIP); ppIRExpr( irsb->next ); vex_printf( "; exit-"); |
|
From: Ivo R. <ir...@so...> - 2017-08-28 22:26:44
|
https://sourceware.org/git/gitweb.cgi?p=valgrind.git;h=f8bdd9ea500f2d7f84bc4c3348c09b40feeb4c4a commit f8bdd9ea500f2d7f84bc4c3348c09b40feeb4c4a Author: Ivo Raisr <iv...@iv...> Date: Tue Aug 8 06:38:54 2017 +0200 ir_inject.c now plays according to the new rules. Diff: --- VEX/priv/ir_inject.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/VEX/priv/ir_inject.c b/VEX/priv/ir_inject.c index c127aca..249ff30 100644 --- a/VEX/priv/ir_inject.c +++ b/VEX/priv/ir_inject.c @@ -43,7 +43,7 @@ #define binop(kind, a1, a2) IRExpr_Binop(kind, a1, a2) #define triop(kind, a1, a2, a3) IRExpr_Triop(kind, a1, a2, a3) #define qop(kind, a1, a2, a3, a4) IRExpr_Qop(kind, a1, a2, a3, a4) -#define stmt(irsb, st) addStmtToIRSB(irsb, st) +#define stmt(irsb, st) addStmtToIRStmtVec(irsb->stmts, st) /* The IR Injection Control Block. vex_inject_ir will query its contents @@ -188,7 +188,7 @@ store(IRSB *irsb, IREndness endian, HWord haddr, IRExpr *data) /* Inject IR stmts depending on the data provided in the control - block iricb. */ + block iricb. IR statements are injected into main IRStmtVec with ID #0. */ void vex_inject_ir(IRSB *irsb, IREndness endian) { @@ -310,11 +310,14 @@ vex_inject_ir(IRSB *irsb, IREndness endian) if (0) { vex_printf("BEGIN inject\n"); if (iricb.t_result == Ity_I1 || sizeofIRType(iricb.t_result) <= 8) { - ppIRStmt(irsb->stmts[irsb->stmts_used - 1]); + ppIRStmt(irsb->stmts->stmts[irsb->stmts->stmts_used - 1], + irsb->tyenv, 0); } else if (sizeofIRType(iricb.t_result) == 16) { - ppIRStmt(irsb->stmts[irsb->stmts_used - 2]); + ppIRStmt(irsb->stmts->stmts[irsb->stmts->stmts_used - 2], + irsb->tyenv, 0); vex_printf("\n"); - ppIRStmt(irsb->stmts[irsb->stmts_used - 1]); + ppIRStmt(irsb->stmts->stmts[irsb->stmts->stmts_used - 1], + irsb->tyenv, 0); } vex_printf("\nEND inject\n"); } |
|
From: Ivo R. <ir...@so...> - 2017-08-28 22:26:43
|
https://sourceware.org/git/gitweb.cgi?p=valgrind.git;h=5dc907113bcfaddee364137bf579dd43524858f4 commit 5dc907113bcfaddee364137bf579dd43524858f4 Author: Ivo Raisr <iv...@iv...> Date: Tue Aug 8 06:31:55 2017 +0200 Support If-Then-Else and Phi nodes in the IR optimizer. Diff: --- VEX/priv/ir_opt.c | 1562 +++++++++++++++++++++++++++++++++-------------------- VEX/priv/ir_opt.h | 3 + 2 files changed, 987 insertions(+), 578 deletions(-) diff --git a/VEX/priv/ir_opt.c b/VEX/priv/ir_opt.c index f40870b..e0c0fcf 100644 --- a/VEX/priv/ir_opt.c +++ b/VEX/priv/ir_opt.c @@ -266,7 +266,7 @@ static void addToHHW ( HashHW* h, HWord key, HWord val ) /* Non-critical helper, heuristic for reducing the number of tmp-tmp copies made by flattening. If in doubt return False. */ -static Bool isFlat ( IRExpr* e ) +static Bool isFlat(const IRExpr* e) { if (e->tag == Iex_Get) return True; @@ -280,102 +280,101 @@ static Bool isFlat ( IRExpr* e ) /* Flatten out 'ex' so it is atomic, returning a new expression with the same value, after having appended extra IRTemp assignments to - the end of 'bb'. */ + the end of 'stmts'. */ -static IRExpr* flatten_Expr ( IRSB* bb, IRExpr* ex ) +static IRExpr* flatten_Expr(IRTypeEnv* tyenv, IRStmtVec* stmts, IRExpr* ex) { Int i; IRExpr** newargs; - IRType ty = typeOfIRExpr(bb->tyenv, ex); + IRType ty = typeOfIRExpr(tyenv, ex); IRTemp t1; switch (ex->tag) { case Iex_GetI: - t1 = newIRTemp(bb->tyenv, ty); - addStmtToIRSB(bb, IRStmt_WrTmp(t1, + t1 = newIRTemp(tyenv, stmts, ty); + addStmtToIRStmtVec(stmts, IRStmt_WrTmp(t1, IRExpr_GetI(ex->Iex.GetI.descr, - flatten_Expr(bb, ex->Iex.GetI.ix), + flatten_Expr(tyenv, stmts, ex->Iex.GetI.ix), ex->Iex.GetI.bias))); return IRExpr_RdTmp(t1); case Iex_Get: - t1 = newIRTemp(bb->tyenv, ty); - addStmtToIRSB(bb, - IRStmt_WrTmp(t1, ex)); + t1 = newIRTemp(tyenv, stmts, ty); + addStmtToIRStmtVec(stmts, IRStmt_WrTmp(t1, ex)); return IRExpr_RdTmp(t1); case Iex_Qop: { IRQop* qop = ex->Iex.Qop.details; - t1 = newIRTemp(bb->tyenv, ty); - addStmtToIRSB(bb, IRStmt_WrTmp(t1, + t1 = newIRTemp(tyenv, stmts, ty); + addStmtToIRStmtVec(stmts, IRStmt_WrTmp(t1, IRExpr_Qop(qop->op, - flatten_Expr(bb, qop->arg1), - flatten_Expr(bb, qop->arg2), - flatten_Expr(bb, qop->arg3), - flatten_Expr(bb, qop->arg4)))); + flatten_Expr(tyenv, stmts, qop->arg1), + flatten_Expr(tyenv, stmts, qop->arg2), + flatten_Expr(tyenv, stmts, qop->arg3), + flatten_Expr(tyenv, stmts, qop->arg4)))); return IRExpr_RdTmp(t1); } case Iex_Triop: { IRTriop* triop = ex->Iex.Triop.details; - t1 = newIRTemp(bb->tyenv, ty); - addStmtToIRSB(bb, IRStmt_WrTmp(t1, + t1 = newIRTemp(tyenv, stmts, ty); + addStmtToIRStmtVec(stmts, IRStmt_WrTmp(t1, IRExpr_Triop(triop->op, - flatten_Expr(bb, triop->arg1), - flatten_Expr(bb, triop->arg2), - flatten_Expr(bb, triop->arg3)))); + flatten_Expr(tyenv, stmts, triop->arg1), + flatten_Expr(tyenv, stmts, triop->arg2), + flatten_Expr(tyenv, stmts, triop->arg3)))); return IRExpr_RdTmp(t1); } case Iex_Binop: - t1 = newIRTemp(bb->tyenv, ty); - addStmtToIRSB(bb, IRStmt_WrTmp(t1, + t1 = newIRTemp(tyenv, stmts, ty); + addStmtToIRStmtVec(stmts, IRStmt_WrTmp(t1, IRExpr_Binop(ex->Iex.Binop.op, - flatten_Expr(bb, ex->Iex.Binop.arg1), - flatten_Expr(bb, ex->Iex.Binop.arg2)))); + flatten_Expr(tyenv, stmts, ex->Iex.Binop.arg1), + flatten_Expr(tyenv, stmts, ex->Iex.Binop.arg2)))); return IRExpr_RdTmp(t1); case Iex_Unop: - t1 = newIRTemp(bb->tyenv, ty); - addStmtToIRSB(bb, IRStmt_WrTmp(t1, + t1 = newIRTemp(tyenv, stmts, ty); + addStmtToIRStmtVec(stmts, IRStmt_WrTmp(t1, IRExpr_Unop(ex->Iex.Unop.op, - flatten_Expr(bb, ex->Iex.Unop.arg)))); + flatten_Expr(tyenv, stmts, ex->Iex.Unop.arg)))); return IRExpr_RdTmp(t1); case Iex_Load: - t1 = newIRTemp(bb->tyenv, ty); - addStmtToIRSB(bb, IRStmt_WrTmp(t1, + t1 = newIRTemp(tyenv, stmts, ty); + addStmtToIRStmtVec(stmts, IRStmt_WrTmp(t1, IRExpr_Load(ex->Iex.Load.end, ex->Iex.Load.ty, - flatten_Expr(bb, ex->Iex.Load.addr)))); + flatten_Expr(tyenv, stmts, ex->Iex.Load.addr)))); return IRExpr_RdTmp(t1); case Iex_CCall: newargs = shallowCopyIRExprVec(ex->Iex.CCall.args); for (i = 0; newargs[i]; i++) - newargs[i] = flatten_Expr(bb, newargs[i]); - t1 = newIRTemp(bb->tyenv, ty); - addStmtToIRSB(bb, IRStmt_WrTmp(t1, + newargs[i] = flatten_Expr(tyenv, stmts, newargs[i]); + t1 = newIRTemp(tyenv, stmts, ty); + addStmtToIRStmtVec(stmts, IRStmt_WrTmp(t1, IRExpr_CCall(ex->Iex.CCall.cee, ex->Iex.CCall.retty, newargs))); return IRExpr_RdTmp(t1); case Iex_ITE: - t1 = newIRTemp(bb->tyenv, ty); - addStmtToIRSB(bb, IRStmt_WrTmp(t1, - IRExpr_ITE(flatten_Expr(bb, ex->Iex.ITE.cond), - flatten_Expr(bb, ex->Iex.ITE.iftrue), - flatten_Expr(bb, ex->Iex.ITE.iffalse)))); + t1 = newIRTemp(tyenv, stmts, ty); + addStmtToIRStmtVec(stmts, IRStmt_WrTmp(t1, + IRExpr_ITE(flatten_Expr(tyenv, stmts, ex->Iex.ITE.cond), + flatten_Expr(tyenv, stmts, ex->Iex.ITE.iftrue), + flatten_Expr(tyenv, stmts, ex->Iex.ITE.iffalse)))); return IRExpr_RdTmp(t1); case Iex_Const: /* Lift F64i constants out onto temps so they can be CSEd later. */ if (ex->Iex.Const.con->tag == Ico_F64i) { - t1 = newIRTemp(bb->tyenv, ty); - addStmtToIRSB(bb, IRStmt_WrTmp(t1, + t1 = newIRTemp(tyenv, stmts, ty); + addStmtToIRStmtVec(stmts, IRStmt_WrTmp(t1, IRExpr_Const(ex->Iex.Const.con))); return IRExpr_RdTmp(t1); } else { @@ -394,10 +393,12 @@ static IRExpr* flatten_Expr ( IRSB* bb, IRExpr* ex ) } } +static IRStmtVec* flatten_IRStmtVec(IRTypeEnv* tyenv, IRStmtVec* in, + IRStmtVec* parent); -/* Append a completely flattened form of 'st' to the end of 'bb'. */ - -static void flatten_Stmt ( IRSB* bb, IRStmt* st ) +/* Append a completely flattened form of 'st' to the end of 'stmts'. */ +static void flatten_Stmt(IRTypeEnv* tyenv, IRStmtVec* stmts, IRStmt* st, + IRStmtVec* parent) { Int i; IRExpr *e1, *e2, *e3, *e4, *e5; @@ -411,69 +412,69 @@ static void flatten_Stmt ( IRSB* bb, IRStmt* st ) if (isIRAtom(st->Ist.Put.data)) { /* optimisation to reduce the amount of heap wasted by the flattener */ - addStmtToIRSB(bb, st); + addStmtToIRStmtVec(stmts, st); } else { /* general case, always correct */ - e1 = flatten_Expr(bb, st->Ist.Put.data); - addStmtToIRSB(bb, IRStmt_Put(st->Ist.Put.offset, e1)); + e1 = flatten_Expr(tyenv, stmts, st->Ist.Put.data); + addStmtToIRStmtVec(stmts, IRStmt_Put(st->Ist.Put.offset, e1)); } break; case Ist_PutI: puti = st->Ist.PutI.details; - e1 = flatten_Expr(bb, puti->ix); - e2 = flatten_Expr(bb, puti->data); + e1 = flatten_Expr(tyenv, stmts, puti->ix); + e2 = flatten_Expr(tyenv, stmts, puti->data); puti2 = mkIRPutI(puti->descr, e1, puti->bias, e2); - addStmtToIRSB(bb, IRStmt_PutI(puti2)); + addStmtToIRStmtVec(stmts, IRStmt_PutI(puti2)); break; case Ist_WrTmp: if (isFlat(st->Ist.WrTmp.data)) { /* optimisation, to reduce the number of tmp-tmp copies generated */ - addStmtToIRSB(bb, st); + addStmtToIRStmtVec(stmts, st); } else { /* general case, always correct */ - e1 = flatten_Expr(bb, st->Ist.WrTmp.data); - addStmtToIRSB(bb, IRStmt_WrTmp(st->Ist.WrTmp.tmp, e1)); + e1 = flatten_Expr(tyenv, stmts, st->Ist.WrTmp.data); + addStmtToIRStmtVec(stmts, IRStmt_WrTmp(st->Ist.WrTmp.tmp, e1)); } break; case Ist_Store: - e1 = flatten_Expr(bb, st->Ist.Store.addr); - e2 = flatten_Expr(bb, st->Ist.Store.data); - addStmtToIRSB(bb, IRStmt_Store(st->Ist.Store.end, e1,e2)); + e1 = flatten_Expr(tyenv, stmts, st->Ist.Store.addr); + e2 = flatten_Expr(tyenv, stmts, st->Ist.Store.data); + addStmtToIRStmtVec(stmts, IRStmt_Store(st->Ist.Store.end, e1,e2)); break; case Ist_StoreG: sg = st->Ist.StoreG.details; - e1 = flatten_Expr(bb, sg->addr); - e2 = flatten_Expr(bb, sg->data); - e3 = flatten_Expr(bb, sg->guard); - addStmtToIRSB(bb, IRStmt_StoreG(sg->end, e1, e2, e3)); + e1 = flatten_Expr(tyenv, stmts, sg->addr); + e2 = flatten_Expr(tyenv, stmts, sg->data); + e3 = flatten_Expr(tyenv, stmts, sg->guard); + addStmtToIRStmtVec(stmts, IRStmt_StoreG(sg->end, e1, e2, e3)); break; case Ist_LoadG: lg = st->Ist.LoadG.details; - e1 = flatten_Expr(bb, lg->addr); - e2 = flatten_Expr(bb, lg->alt); - e3 = flatten_Expr(bb, lg->guard); - addStmtToIRSB(bb, IRStmt_LoadG(lg->end, lg->cvt, lg->dst, - e1, e2, e3)); + e1 = flatten_Expr(tyenv, stmts, lg->addr); + e2 = flatten_Expr(tyenv, stmts, lg->alt); + e3 = flatten_Expr(tyenv, stmts, lg->guard); + addStmtToIRStmtVec(stmts, IRStmt_LoadG(lg->end, lg->cvt, lg->dst, + e1, e2, e3)); break; case Ist_CAS: cas = st->Ist.CAS.details; - e1 = flatten_Expr(bb, cas->addr); - e2 = cas->expdHi ? flatten_Expr(bb, cas->expdHi) : NULL; - e3 = flatten_Expr(bb, cas->expdLo); - e4 = cas->dataHi ? flatten_Expr(bb, cas->dataHi) : NULL; - e5 = flatten_Expr(bb, cas->dataLo); + e1 = flatten_Expr(tyenv, stmts, cas->addr); + e2 = cas->expdHi ? flatten_Expr(tyenv, stmts, cas->expdHi) : NULL; + e3 = flatten_Expr(tyenv, stmts, cas->expdLo); + e4 = cas->dataHi ? flatten_Expr(tyenv, stmts, cas->dataHi) : NULL; + e5 = flatten_Expr(tyenv, stmts, cas->dataLo); cas2 = mkIRCAS( cas->oldHi, cas->oldLo, cas->end, e1, e2, e3, e4, e5 ); - addStmtToIRSB(bb, IRStmt_CAS(cas2)); + addStmtToIRStmtVec(stmts, IRStmt_CAS(cas2)); break; case Ist_LLSC: - e1 = flatten_Expr(bb, st->Ist.LLSC.addr); + e1 = flatten_Expr(tyenv, stmts, st->Ist.LLSC.addr); e2 = st->Ist.LLSC.storedata - ? flatten_Expr(bb, st->Ist.LLSC.storedata) + ? flatten_Expr(tyenv, stmts, st->Ist.LLSC.storedata) : NULL; - addStmtToIRSB(bb, IRStmt_LLSC(st->Ist.LLSC.end, - st->Ist.LLSC.result, e1, e2)); + addStmtToIRStmtVec(stmts, IRStmt_LLSC(st->Ist.LLSC.end, + st->Ist.LLSC.result, e1, e2)); break; case Ist_Dirty: d = st->Ist.Dirty.details; @@ -481,53 +482,72 @@ static void flatten_Stmt ( IRSB* bb, IRStmt* st ) *d2 = *d; d2->args = shallowCopyIRExprVec(d2->args); if (d2->mFx != Ifx_None) { - d2->mAddr = flatten_Expr(bb, d2->mAddr); + d2->mAddr = flatten_Expr(tyenv, stmts, d2->mAddr); } else { vassert(d2->mAddr == NULL); } - d2->guard = flatten_Expr(bb, d2->guard); + d2->guard = flatten_Expr(tyenv, stmts, d2->guard); for (i = 0; d2->args[i]; i++) { IRExpr* arg = d2->args[i]; if (LIKELY(!is_IRExpr_VECRET_or_GSPTR(arg))) - d2->args[i] = flatten_Expr(bb, arg); + d2->args[i] = flatten_Expr(tyenv, stmts, arg); } - addStmtToIRSB(bb, IRStmt_Dirty(d2)); + addStmtToIRStmtVec(stmts, IRStmt_Dirty(d2)); break; case Ist_NoOp: case Ist_MBE: case Ist_IMark: - addStmtToIRSB(bb, st); + addStmtToIRStmtVec(stmts, st); break; case Ist_AbiHint: - e1 = flatten_Expr(bb, st->Ist.AbiHint.base); - e2 = flatten_Expr(bb, st->Ist.AbiHint.nia); - addStmtToIRSB(bb, IRStmt_AbiHint(e1, st->Ist.AbiHint.len, e2)); + e1 = flatten_Expr(tyenv, stmts, st->Ist.AbiHint.base); + e2 = flatten_Expr(tyenv, stmts, st->Ist.AbiHint.nia); + addStmtToIRStmtVec(stmts, IRStmt_AbiHint(e1, st->Ist.AbiHint.len, e2)); break; case Ist_Exit: - e1 = flatten_Expr(bb, st->Ist.Exit.guard); - addStmtToIRSB(bb, IRStmt_Exit(e1, st->Ist.Exit.jk, - st->Ist.Exit.dst, - st->Ist.Exit.offsIP)); + e1 = flatten_Expr(tyenv, stmts, st->Ist.Exit.guard); + addStmtToIRStmtVec(stmts, IRStmt_Exit(e1, st->Ist.Exit.jk, + st->Ist.Exit.dst, + st->Ist.Exit.offsIP)); + break; + case Ist_IfThenElse: { + IRIfThenElse* ite = st->Ist.IfThenElse.details; + e1 = flatten_Expr(tyenv, stmts, ite->cond); + addStmtToIRStmtVec(stmts, + IRStmt_IfThenElse(e1, ite->hint, + flatten_IRStmtVec(tyenv, ite->then_leg, parent), + flatten_IRStmtVec(tyenv, ite->else_leg, parent), + ite->phi_nodes)); break; + } default: vex_printf("\n"); - ppIRStmt(st); + ppIRStmt(st, tyenv, 0); vex_printf("\n"); vpanic("flatten_Stmt"); } } +static IRStmtVec* flatten_IRStmtVec(IRTypeEnv* tyenv, IRStmtVec* in, + IRStmtVec* parent) +{ + IRStmtVec* out = emptyIRStmtVec(); + out->parent = parent; + out->id = in->id; + out->defset = deepCopyIRTempDefSet(in->defset); + for (UInt i = 0; i < in->stmts_used; i++) { + flatten_Stmt(tyenv, out, in->stmts[i], out); + } + return out; +} static IRSB* flatten_BB ( IRSB* in ) { - Int i; - IRSB* out; - out = emptyIRSB(); - out->tyenv = deepCopyIRTypeEnv( in->tyenv ); - for (i = 0; i < in->stmts_used; i++) - if (in->stmts[i]) - flatten_Stmt( out, in->stmts[i] ); - out->next = flatten_Expr( out, in->next ); + IRSB* out = emptyIRSB(); + out->tyenv = deepCopyIRTypeEnv(in->tyenv); + out->id_seq = in->id_seq; + out->stmts = flatten_IRStmtVec(out->tyenv, in->stmts, NULL); + out->next = flatten_Expr(out->tyenv, out->stmts, in->next); out->jumpkind = in->jumpkind; out->offsIP = in->offsIP; return out; @@ -610,16 +630,16 @@ static void invalidateOverlaps ( HashHW* h, UInt k_lo, UInt k_hi ) } } - -static void redundant_get_removal_BB ( IRSB* bb ) +static +void redundant_get_removal_IRStmtVec(const IRTypeEnv* tyenv, IRStmtVec* stmts) { HashHW* env = newHHW(); UInt key = 0; /* keep gcc -O happy */ - Int i, j; + Int j; HWord val; - for (i = 0; i < bb->stmts_used; i++) { - IRStmt* st = bb->stmts[i]; + for (UInt i = 0; i < stmts->stmts_used; i++) { + IRStmt* st = stmts->stmts[i]; if (st->tag == Ist_NoOp) continue; @@ -640,7 +660,7 @@ static void redundant_get_removal_BB ( IRSB* bb ) be to stick in a reinterpret-style cast, although that would make maintaining flatness more difficult. */ IRExpr* valE = (IRExpr*)val; - Bool typesOK = toBool( typeOfIRExpr(bb->tyenv,valE) + Bool typesOK = toBool( typeOfIRExpr(tyenv, valE) == st->Ist.WrTmp.data->Iex.Get.ty ); if (typesOK && DEBUG_IROPT) { vex_printf("rGET: "); ppIRExpr(get); @@ -648,7 +668,7 @@ static void redundant_get_removal_BB ( IRSB* bb ) vex_printf("\n"); } if (typesOK) - bb->stmts[i] = IRStmt_WrTmp(st->Ist.WrTmp.tmp, valE); + stmts->stmts[i] = IRStmt_WrTmp(st->Ist.WrTmp.tmp, valE); } else { /* Not found, but at least we know that t and the Get(...) are now associated. So add a binding to reflect that @@ -664,7 +684,7 @@ static void redundant_get_removal_BB ( IRSB* bb ) UInt k_lo, k_hi; if (st->tag == Ist_Put) { key = mk_key_GetPut( st->Ist.Put.offset, - typeOfIRExpr(bb->tyenv,st->Ist.Put.data) ); + typeOfIRExpr(tyenv, st->Ist.Put.data) ); } else { vassert(st->tag == Ist_PutI); key = mk_key_GetIPutI( st->Ist.PutI.details->descr ); @@ -700,8 +720,19 @@ static void redundant_get_removal_BB ( IRSB* bb ) addToHHW( env, (HWord)key, (HWord)(st->Ist.Put.data)); } - } /* for (i = 0; i < bb->stmts_used; i++) */ + if (st->tag == Ist_IfThenElse) { + /* Consider "then" and "else" legs in isolation. */ + IRIfThenElse* ite = st->Ist.IfThenElse.details; + redundant_get_removal_IRStmtVec(tyenv, ite->then_leg); + redundant_get_removal_IRStmtVec(tyenv, ite->else_leg); + } + + } /* for (UInt i = 0; i < stmts->stmts_used; i++) */ +} +static void redundant_get_removal_BB(IRSB* bb) +{ + redundant_get_removal_IRStmtVec(bb->tyenv, bb->stmts); } @@ -713,8 +744,8 @@ static void redundant_get_removal_BB ( IRSB* bb ) overlapping ranges listed in env. Due to the flattening phase, the only stmt kind we expect to find a Get on is IRStmt_WrTmp. */ -static void handle_gets_Stmt ( - HashHW* env, +static void handle_gets_Stmt ( + HashHW* env, IRStmt* st, Bool (*preciseMemExnsFn)(Int,Int,VexRegisterUpdates), VexRegisterUpdates pxControl @@ -817,9 +848,14 @@ static void handle_gets_Stmt ( case Ist_IMark: break; + case Ist_IfThenElse: + /* Recursing into "then" and "else" branches is done in + redundant_put_removal_IRStmtVec() */ + break; + default: vex_printf("\n"); - ppIRStmt(st); + ppIRStmt(st, NULL, 0); vex_printf("\n"); vpanic("handle_gets_Stmt"); } @@ -882,30 +918,17 @@ static void handle_gets_Stmt ( and loads/stores. */ -static void redundant_put_removal_BB ( - IRSB* bb, +static void redundant_put_removal_IRStmtVec( + IRTypeEnv* tyenv, IRStmtVec* stmts, Bool (*preciseMemExnsFn)(Int,Int,VexRegisterUpdates), - VexRegisterUpdates pxControl - ) + VexRegisterUpdates pxControl, + HashHW* env) { - Int i, j; - Bool isPut; - IRStmt* st; - UInt key = 0; /* keep gcc -O happy */ - - vassert(pxControl < VexRegUpdAllregsAtEachInsn); - - HashHW* env = newHHW(); - - /* Initialise the running env with the fact that the final exit - writes the IP (or, whatever it claims to write. We don't - care.) */ - key = mk_key_GetPut(bb->offsIP, typeOfIRExpr(bb->tyenv, bb->next)); - addToHHW(env, (HWord)key, 0); - /* And now scan backwards through the statements. */ - for (i = bb->stmts_used-1; i >= 0; i--) { - st = bb->stmts[i]; + for (Int i = stmts->stmts_used - 1; i >= 0; i--) { + IRStmt* st = stmts->stmts[i]; + Bool isPut; + UInt key; if (st->tag == Ist_NoOp) continue; @@ -933,7 +956,7 @@ static void redundant_put_removal_BB ( // typeOfIRConst(st->Ist.Exit.dst)); //re_add = lookupHHW(env, NULL, key); /* (2) */ - for (j = 0; j < env->used; j++) + for (UInt j = 0; j < env->used; j++) env->inuse[j] = False; /* (3) */ //if (0 && re_add) @@ -946,7 +969,7 @@ static void redundant_put_removal_BB ( case Ist_Put: isPut = True; key = mk_key_GetPut( st->Ist.Put.offset, - typeOfIRExpr(bb->tyenv,st->Ist.Put.data) ); + typeOfIRExpr(tyenv, st->Ist.Put.data) ); vassert(isIRAtom(st->Ist.Put.data)); break; case Ist_PutI: @@ -968,10 +991,10 @@ static void redundant_put_removal_BB ( /* This Put is redundant because a later one will overwrite it. So NULL (nop) it out. */ if (DEBUG_IROPT) { - vex_printf("rPUT: "); ppIRStmt(st); + vex_printf("rPUT: "); ppIRStmt(st, tyenv, 0); vex_printf("\n"); } - bb->stmts[i] = IRStmt_NoOp(); + stmts->stmts[i] = IRStmt_NoOp(); } else { /* We can't demonstrate that this Put is redundant, so add it to the running collection. */ @@ -986,9 +1009,38 @@ static void redundant_put_removal_BB ( deals with implicit reads of guest state needed to maintain precise exceptions. */ handle_gets_Stmt( env, st, preciseMemExnsFn, pxControl ); + + /* Consider "then" and "else" legs in isolation. They get a new env. */ + if (st->tag == Ist_IfThenElse) { + IRIfThenElse* ite = st->Ist.IfThenElse.details; + redundant_put_removal_IRStmtVec(tyenv, ite->then_leg, preciseMemExnsFn, + pxControl, newHHW()); + redundant_put_removal_IRStmtVec(tyenv, ite->else_leg, preciseMemExnsFn, + pxControl, newHHW()); + } } } +static void redundant_put_removal_BB( + IRSB* bb, + Bool (*preciseMemExnsFn)(Int,Int,VexRegisterUpdates), + VexRegisterUpdates pxControl) +{ + vassert(pxControl < VexRegUpdAllregsAtEachInsn); + + HashHW* env = newHHW(); + + /* Initialise the running env with the fact that the final exit + writes the IP (or, whatever it claims to write. We don't + care.) */ + UInt key = mk_key_GetPut(bb->offsIP, + typeOfIRExpr(bb->tyenv, bb->next)); + addToHHW(env, (HWord)key, 0); + + redundant_put_removal_IRStmtVec(bb->tyenv, bb->stmts, preciseMemExnsFn, + pxControl, env); +} + /*---------------------------------------------------------------*/ /*--- Constant propagation and folding ---*/ @@ -1023,8 +1075,43 @@ static UInt num_nodes_visited; #define NODE_LIMIT 30 -/* The env in this section is a map from IRTemp to IRExpr*, - that is, an array indexed by IRTemp. */ +/* The env in this section is a structure which holds: + - A map from IRTemp to IRExpr*, that is, an array indexed by IRTemp. + Keys are IRTemp.indices. Values are IRExpr*s. + - IR Type Environment + - Current IRStmtVec* which is being constructed. + - A pointer to the parent env (or NULL). */ +typedef + struct _SubstEnv { + IRExpr** map; + IRTypeEnv* tyenv; + IRStmtVec* stmts; + struct _SubstEnv* parent; + } + SubstEnv; + +/* Sets up the substitution environment. + Note that the map is established fresh new for every IRStmtVec (which are + thus considered in isolation). */ +static SubstEnv* newSubstEnv(IRTypeEnv* tyenv, IRStmtVec* stmts_in, + SubstEnv* parent_env) +{ + IRStmtVec* stmts_out = emptyIRStmtVec(); + stmts_out->id = stmts_in->id; + stmts_out->parent = (parent_env != NULL) ? parent_env->stmts : NULL; + stmts_out->defset = deepCopyIRTempDefSet(stmts_in->defset); + + SubstEnv* env = LibVEX_Alloc_inline(sizeof(SubstEnv)); + env->tyenv = tyenv; + env->stmts = stmts_out; + env->parent = parent_env; + + UInt n_tmps = tyenv->used; + env->map = LibVEX_Alloc_inline(n_tmps * sizeof(IRExpr*)); + for (UInt i = 0; i < n_tmps; i++) + env->map[i] = (parent_env == NULL) ? NULL : parent_env->map[i]; + return env; +} /* Do both expressions compute the same value? The answer is generally conservative, i.e. it will report that the expressions do not compute @@ -1043,27 +1130,33 @@ static UInt num_nodes_visited; slower out of line general case. Saves a few insns. */ __attribute__((noinline)) -static Bool sameIRExprs_aux2 ( IRExpr** env, IRExpr* e1, IRExpr* e2 ); +static Bool sameIRExprs_aux2(const SubstEnv* env, const IRExpr* e1, + const IRExpr* e2); inline -static Bool sameIRExprs_aux ( IRExpr** env, IRExpr* e1, IRExpr* e2 ) +static Bool sameIRExprs_aux(const SubstEnv* env, const IRExpr* e1, + const IRExpr* e2) { if (e1->tag != e2->tag) return False; return sameIRExprs_aux2(env, e1, e2); } __attribute__((noinline)) -static Bool sameIRExprs_aux2 ( IRExpr** env, IRExpr* e1, IRExpr* e2 ) +static Bool sameIRExprs_aux2(const SubstEnv* env, const IRExpr* e1, + const IRExpr* e2) { if (num_nodes_visited++ > NODE_LIMIT) return False; switch (e1->tag) { - case Iex_RdTmp: - if (e1->Iex.RdTmp.tmp == e2->Iex.RdTmp.tmp) return True; - - if (env[e1->Iex.RdTmp.tmp] && env[e2->Iex.RdTmp.tmp]) { - Bool same = sameIRExprs_aux(env, env[e1->Iex.RdTmp.tmp], - env[e2->Iex.RdTmp.tmp]); + case Iex_RdTmp: { + IRTemp tmp1 = e1->Iex.RdTmp.tmp; + IRTemp tmp2 = e2->Iex.RdTmp.tmp; + + if (tmp1 == tmp2) return True; + const IRExpr* subst1 = env->map[tmp1]; + const IRExpr* subst2 = env->map[tmp2]; + if (subst1 != NULL && subst2 != NULL) { + Bool same = sameIRExprs_aux(env, subst1, subst2); #if STATS_IROPT recursed = True; if (same) recursion_helped = True; @@ -1071,6 +1164,7 @@ static Bool sameIRExprs_aux2 ( IRExpr** env, IRExpr* e1, IRExpr* e2 ) return same; } return False; + } case Iex_Get: case Iex_GetI: @@ -1131,7 +1225,7 @@ static Bool sameIRExprs_aux2 ( IRExpr** env, IRExpr* e1, IRExpr* e2 ) } inline -static Bool sameIRExprs ( IRExpr** env, IRExpr* e1, IRExpr* e2 ) +static Bool sameIRExprs(const SubstEnv* env, const IRExpr* e1, const IRExpr* e2) { Bool same; @@ -1160,8 +1254,8 @@ static Bool sameIRExprs ( IRExpr** env, IRExpr* e1, IRExpr* e2 ) --vex-iropt-level > 0, that is, vex_control.iropt_verbosity > 0. Bad because it duplicates functionality from typeOfIRExpr. See comment on the single use point below for rationale. */ -static -Bool debug_only_hack_sameIRExprs_might_assert ( IRExpr* e1, IRExpr* e2 ) +static Bool +debug_only_hack_sameIRExprs_might_assert(const IRExpr* e1, const IRExpr* e2) { if (e1->tag != e2->tag) return False; switch (e1->tag) { @@ -1179,7 +1273,7 @@ Bool debug_only_hack_sameIRExprs_might_assert ( IRExpr* e1, IRExpr* e2 ) /* Is this literally IRExpr_Const(IRConst_U32(0)) ? */ -static Bool isZeroU32 ( IRExpr* e ) +static Bool isZeroU32(const IRExpr* e) { return toBool( e->tag == Iex_Const && e->Iex.Const.con->tag == Ico_U32 @@ -1189,7 +1283,7 @@ static Bool isZeroU32 ( IRExpr* e ) /* Is this literally IRExpr_Const(IRConst_U64(0)) ? Currently unused; commented out to avoid compiler warning */ #if 0 -static Bool isZeroU64 ( IRExpr* e ) +static Bool isZeroU64(const IRExpr* e) { return toBool( e->tag == Iex_Const && e->Iex.Const.con->tag == Ico_U64 @@ -1198,7 +1292,7 @@ static Bool isZeroU64 ( IRExpr* e ) #endif /* Is this literally IRExpr_Const(IRConst_V128(0)) ? */ -static Bool isZeroV128 ( IRExpr* e ) +static Bool isZeroV128(const IRExpr* e) { return toBool( e->tag == Iex_Const && e->Iex.Const.con->tag == Ico_V128 @@ -1206,7 +1300,7 @@ static Bool isZeroV128 ( IRExpr* e ) } /* Is this literally IRExpr_Const(IRConst_V256(0)) ? */ -static Bool isZeroV256 ( IRExpr* e ) +static Bool isZeroV256(const IRExpr* e) { return toBool( e->tag == Iex_Const && e->Iex.Const.con->tag == Ico_V256 @@ -1214,7 +1308,7 @@ static Bool isZeroV256 ( IRExpr* e ) } /* Is this an integer constant with value 0 ? */ -static Bool isZeroU ( IRExpr* e ) +static Bool isZeroU(const IRExpr* e) { if (e->tag != Iex_Const) return False; switch (e->Iex.Const.con->tag) { @@ -1229,7 +1323,7 @@ static Bool isZeroU ( IRExpr* e ) } /* Is this an integer constant with value 1---1b ? */ -static Bool isOnesU ( IRExpr* e ) +static Bool isOnesU(const IRExpr* e) { if (e->tag != Iex_Const) return False; switch (e->Iex.Const.con->tag) { @@ -1346,29 +1440,29 @@ static UInt fold_Clz32 ( UInt value ) return NULL if it can't resolve 'e' to a new expression, which will be the case if 'e' is instead defined by an IRStmt (IRDirty or LLSC). */ -static IRExpr* chase ( IRExpr** env, IRExpr* e ) +static IRExpr* chase(SubstEnv* env, IRExpr* e) { /* Why is this loop guaranteed to terminate? Because all tmps must have definitions before use, hence a tmp cannot be bound (directly or indirectly) to itself. */ while (e->tag == Iex_RdTmp) { if (0) { vex_printf("chase "); ppIRExpr(e); vex_printf("\n"); } - e = env[(Int)e->Iex.RdTmp.tmp]; + e = env->map[e->Iex.RdTmp.tmp]; if (e == NULL) break; } return e; } /* Similar to |chase|, but follows at most one level of tmp reference. */ -static IRExpr* chase1 ( IRExpr** env, IRExpr* e ) +static IRExpr* chase1(IRExpr* env[], IRExpr* e) { if (e == NULL || e->tag != Iex_RdTmp) return e; else - return env[(Int)e->Iex.RdTmp.tmp]; + return env[e->Iex.RdTmp.tmp]; } -static IRExpr* fold_Expr ( IRExpr** env, IRExpr* e ) +static IRExpr* fold_Expr(SubstEnv* env, IRExpr* e) { Int shift; IRExpr* e2 = e; /* e2 is the result of folding e, if possible */ @@ -2428,13 +2522,12 @@ static IRExpr* fold_Expr ( IRExpr** env, IRExpr* e ) /* Apply the subst to a simple 1-level expression -- guaranteed to be 1-level due to previous flattening pass. */ - -static IRExpr* subst_Expr ( IRExpr** env, IRExpr* ex ) +static IRExpr* subst_Expr(SubstEnv* env, IRExpr* ex) { switch (ex->tag) { - case Iex_RdTmp: - if (env[(Int)ex->Iex.RdTmp.tmp] != NULL) { - IRExpr *rhs = env[(Int)ex->Iex.RdTmp.tmp]; + case Iex_RdTmp: { + IRExpr* rhs = env->map[ex->Iex.RdTmp.tmp]; + if (rhs != NULL) { if (rhs->tag == Iex_RdTmp) return rhs; if (rhs->tag == Iex_Const @@ -2443,6 +2536,7 @@ static IRExpr* subst_Expr ( IRExpr** env, IRExpr* ex ) } /* not bound in env */ return ex; + } case Iex_Const: case Iex_Get: @@ -2539,16 +2633,39 @@ static IRExpr* subst_Expr ( IRExpr** env, IRExpr* ex ) } } +/* A phi node of the form dst = phi(srcThen, srcElse) behaves much like + a WrTmp. Ensure that the connection between src{Then,Else} and assignments + in the corresponding leg is not broken: + - either add WrTemp assignment for src{Then,Else} in the leg, or + - adjust src{Then,Else} for the phi node. */ +static void subst_and_fold_PhiNodes(SubstEnv* env, IRStmtVec* stmts, + Bool srcThen, IRPhiVec* phi_nodes) +{ + for (UInt i = 0; i < phi_nodes->phis_used; i++) { + IRPhi* phi = phi_nodes->phis[i]; + IRTemp* tmp = (srcThen) ? &phi->srcThen : &phi->srcElse; + IRExpr* expr = env->map[*tmp]; + vassert(expr != NULL); + + if (expr->tag == Iex_RdTmp) { + *tmp = expr->Iex.RdTmp.tmp; + } else { + addStmtToIRStmtVec(stmts, IRStmt_WrTmp(*tmp, expr)); + } + } +} + +static IRStmtVec* subst_and_fold_Stmts(SubstEnv* env, IRStmtVec* in); /* Apply the subst to stmt, then fold the result as much as possible. Much simplified due to stmt being previously flattened. As a result of this, the stmt may wind up being turned into a no-op. */ -static IRStmt* subst_and_fold_Stmt ( IRExpr** env, IRStmt* st ) +static IRStmt* subst_and_fold_Stmt(SubstEnv* env, IRStmt* st) { # if 0 vex_printf("\nsubst and fold stmt\n"); - ppIRStmt(st); + ppIRStmt(st, env->tyenv, 0); vex_printf("\n"); # endif @@ -2739,7 +2856,7 @@ static IRStmt* subst_and_fold_Stmt ( IRExpr** env, IRStmt* st ) at this point, which is tricky. Such truncation is done later by the dead-code elimination pass. */ /* fall out into the reconstruct-the-exit code. */ - if (vex_control.iropt_verbosity > 0) + if (vex_control.iropt_verbosity > 0) /* really a misuse of vex_control.iropt_verbosity */ vex_printf("vex iropt: IRStmt_Exit became unconditional\n"); } @@ -2748,51 +2865,80 @@ static IRStmt* subst_and_fold_Stmt ( IRExpr** env, IRStmt* st ) st->Ist.Exit.dst, st->Ist.Exit.offsIP); } + case Ist_IfThenElse: { + IRIfThenElse* ite = st->Ist.IfThenElse.details; + vassert(isIRAtom(ite->cond)); + IRExpr *fcond = fold_Expr(env, subst_Expr(env, ite->cond)); + if (fcond->tag == Iex_Const) { + /* Interesting. The condition on this "if-then-else" has folded down + to a constant. */ + vassert(fcond->Iex.Const.con->tag == Ico_U1); + if (fcond->Iex.Const.con->Ico.U1 == True) { + /* TODO-JIT: "else" leg is never going to happen, so dump it. */ + if (vex_control.iropt_verbosity > 0) + vex_printf("vex iropt: IRStmt_IfThenElse became " + "unconditional\n"); + } else { + vassert(fcond->Iex.Const.con->Ico.U1 == False); + /* TODO-JIT: "then" leg is never going to happen, so dump it. */ + if (vex_control.iropt_verbosity > 0) + vex_printf("vex iropt: IRStmt_IfThenElse became " + "unconditional\n"); + } + /* TODO-JIT: Pull the only remaining leg into the current IRStmtVec. + Here is what needs to be done: + 1. Rewrite ID of all IRTemp's (in tyenv->ids) defined in the + pulled leg. These are tracked in leg's defset. + 2. Insert all statements from the leg in the env->stmts_out + at the current position. */ + vpanic("IfThenElse leg lifting unimplemented"); + } + + SubstEnv* then_env = newSubstEnv(env->tyenv, ite->then_leg, env); + IRStmtVec* then_stmts = subst_and_fold_Stmts(then_env, ite->then_leg); + subst_and_fold_PhiNodes(then_env, then_stmts, True /* srcThen */, + ite->phi_nodes); + + SubstEnv* else_env = newSubstEnv(env->tyenv, ite->else_leg, env); + IRStmtVec* else_stmts = subst_and_fold_Stmts(else_env, ite->else_leg); + subst_and_fold_PhiNodes(else_env, else_stmts, False /* srcThen */, + ite->phi_nodes); + + return IRStmt_IfThenElse(fcond, ite->hint, then_stmts, else_stmts, + ite->phi_nodes); + } + default: - vex_printf("\n"); ppIRStmt(st); + vex_printf("\n"); ppIRStmt(st, env->tyenv, 0); vpanic("subst_and_fold_Stmt"); } } - -IRSB* cprop_BB ( IRSB* in ) +/* Is to be called with already created SubstEnv as per newSubstEnv(). */ +static IRStmtVec* subst_and_fold_Stmts(SubstEnv* env, IRStmtVec* in) { - Int i; - IRSB* out; - IRStmt* st2; - Int n_tmps = in->tyenv->types_used; - IRExpr** env = LibVEX_Alloc_inline(n_tmps * sizeof(IRExpr*)); /* Keep track of IRStmt_LoadGs that we need to revisit after processing all the other statements. */ const Int N_FIXUPS = 16; Int fixups[N_FIXUPS]; /* indices in the stmt array of 'out' */ Int n_fixups = 0; - out = emptyIRSB(); - out->tyenv = deepCopyIRTypeEnv( in->tyenv ); - - /* Set up the env with which travels forward. This holds a - substitution, mapping IRTemps to IRExprs. The environment - is to be applied as we move along. Keys are IRTemps. - Values are IRExpr*s. - */ - for (i = 0; i < n_tmps; i++) - env[i] = NULL; + IRStmtVec* out = env->stmts; /* For each original SSA-form stmt ... */ - for (i = 0; i < in->stmts_used; i++) { + for (UInt i = 0; i < in->stmts_used; i++) { /* First apply the substitution to the current stmt. This propagates in any constants and tmp-tmp assignments accumulated prior to this point. As part of the subst_Stmt call, also then fold any constant expressions resulting. */ - st2 = in->stmts[i]; + IRStmt* st2 = in->stmts[i]; /* perhaps st2 is already a no-op? */ if (st2->tag == Ist_NoOp) continue; - st2 = subst_and_fold_Stmt( env, st2 ); + st2 = subst_and_fold_Stmt(env, st2); /* Deal with some post-folding special cases. */ switch (st2->tag) { @@ -2806,9 +2952,9 @@ IRSB* cprop_BB ( IRSB* in ) running environment. This is for the benefit of copy propagation and to allow sameIRExpr look through IRTemps. */ - case Ist_WrTmp: { - vassert(env[(Int)(st2->Ist.WrTmp.tmp)] == NULL); - env[(Int)(st2->Ist.WrTmp.tmp)] = st2->Ist.WrTmp.data; + case Ist_WrTmp: + vassert(env->map[st2->Ist.WrTmp.tmp] == NULL); + env->map[st2->Ist.WrTmp.tmp] = st2->Ist.WrTmp.data; /* 't1 = t2' -- don't add to BB; will be optimized out */ if (st2->Ist.WrTmp.data->tag == Iex_RdTmp) @@ -2824,7 +2970,6 @@ IRSB* cprop_BB ( IRSB* in ) } /* else add it to the output, as normal */ break; - } case Ist_LoadG: { IRLoadG* lg = st2->Ist.LoadG.details; @@ -2844,7 +2989,7 @@ IRSB* cprop_BB ( IRSB* in ) vassert(n_fixups >= 0 && n_fixups <= N_FIXUPS); if (n_fixups < N_FIXUPS) { fixups[n_fixups++] = out->stmts_used; - addStmtToIRSB( out, IRStmt_NoOp() ); + addStmtToIRStmtVec(out, IRStmt_NoOp()); } } /* And always add the LoadG to the output, regardless. */ @@ -2855,24 +3000,14 @@ IRSB* cprop_BB ( IRSB* in ) break; } - /* Not interesting, copy st2 into the output block. */ - addStmtToIRSB( out, st2 ); + /* Not interesting, copy st2 into the output vector. */ + addStmtToIRStmtVec(out, st2); } -# if STATS_IROPT - vex_printf("sameIRExpr: invoked = %u/%u equal = %u/%u max_nodes = %u\n", - invocation_count, recursion_count, success_count, - recursion_success_count, max_nodes_visited); -# endif - - out->next = subst_Expr( env, in->next ); - out->jumpkind = in->jumpkind; - out->offsIP = in->offsIP; - /* Process any leftover unconditional LoadGs that we noticed in the main pass. */ vassert(n_fixups >= 0 && n_fixups <= N_FIXUPS); - for (i = 0; i < n_fixups; i++) { + for (UInt i = 0; i < n_fixups; i++) { Int ix = fixups[i]; /* Carefully verify that the LoadG has the expected form. */ vassert(ix >= 0 && ix+1 < out->stmts_used); @@ -2901,7 +3036,7 @@ IRSB* cprop_BB ( IRSB* in ) } /* Replace the placeholder NoOp by the required unconditional load. */ - IRTemp tLoaded = newIRTemp(out->tyenv, cvtArg); + IRTemp tLoaded = newIRTemp(env->tyenv, out, cvtArg); out->stmts[ix] = IRStmt_WrTmp(tLoaded, IRExpr_Load(lg->end, cvtArg, lg->addr)); @@ -2917,6 +3052,26 @@ IRSB* cprop_BB ( IRSB* in ) return out; } +IRSB* cprop_BB ( IRSB* in ) +{ + SubstEnv* env = newSubstEnv(in->tyenv, in->stmts, NULL); + IRSB* out = emptyIRSB(); + out->tyenv = deepCopyIRTypeEnv(in->tyenv); + out->stmts = subst_and_fold_Stmts(env, in->stmts); + out->id_seq = in->id_seq; + out->next = subst_Expr( env, in->next ); + out->jumpkind = in->jumpkind; + out->offsIP = in->offsIP; + +# if STATS_IROPT + vex_printf("sameIRExpr: invoked = %u/%u equal = %u/%u max_nodes = %u\n", + invocation_count, recursion_count, success_count, + recursion_success_count, max_nodes_visited); +# endif + + return out; +} + /*---------------------------------------------------------------*/ /*--- Dead code (t = E) removal ---*/ @@ -2932,7 +3087,7 @@ IRSB* cprop_BB ( IRSB* in ) inline static void addUses_Temp ( Bool* set, IRTemp tmp ) { - set[(Int)tmp] = True; + set[tmp] = True; } static void addUses_Expr ( Bool* set, IRExpr* e ) @@ -2985,9 +3140,11 @@ static void addUses_Expr ( Bool* set, IRExpr* e ) } } +static void do_deadcode_IRStmtVec(Bool* set, IRStmtVec* stmts, + Int* i_unconditional_exit); + static void addUses_Stmt ( Bool* set, IRStmt* st ) { - Int i; IRDirty* d; IRCAS* cas; switch (st->tag) { @@ -3043,7 +3200,7 @@ static void addUses_Stmt ( Bool* set, IRStmt* st ) if (d->mFx != Ifx_None) addUses_Expr(set, d->mAddr); addUses_Expr(set, d->guard); - for (i = 0; d->args[i] != NULL; i++) { + for (UInt i = 0; d->args[i] != NULL; i++) { IRExpr* arg = d->args[i]; if (LIKELY(!is_IRExpr_VECRET_or_GSPTR(arg))) addUses_Expr(set, arg); @@ -3056,9 +3213,26 @@ static void addUses_Stmt ( Bool* set, IRStmt* st ) case Ist_Exit: addUses_Expr(set, st->Ist.Exit.guard); return; + case Ist_IfThenElse: { + IRIfThenElse* ite = st->Ist.IfThenElse.details; + addUses_Expr(set, ite->cond); + + for (UInt i = 0; i < ite->phi_nodes->phis_used; i++) { + const IRPhi* phi = ite->phi_nodes->phis[i]; + addUses_Temp(set, phi->srcThen); + addUses_Temp(set, phi->srcElse); + } + + Int i_unconditional_exit; // TODO-JIT: unused at the moment + /* Consider both legs simultaneously. If either of them reports an + IRTemp in use, then it won't be eliminated. */ + do_deadcode_IRStmtVec(set, ite->then_leg, &i_unconditional_exit); + do_deadcode_IRStmtVec(set, ite->else_leg, &i_unconditional_exit); + return; + } default: vex_printf("\n"); - ppIRStmt(st); + ppIRStmt(st, NULL, 0); vpanic("addUses_Stmt"); } } @@ -3095,40 +3269,27 @@ static Bool isOneU1 ( IRExpr* e ) all statements following it are turned into no-ops. */ -/* notstatic */ void do_deadcode_BB ( IRSB* bb ) +static void do_deadcode_IRStmtVec(Bool* set, IRStmtVec* stmts, + Int* i_unconditional_exit) { - Int i, i_unconditional_exit; - Int n_tmps = bb->tyenv->types_used; - Bool* set = LibVEX_Alloc_inline(n_tmps * sizeof(Bool)); - IRStmt* st; - - for (i = 0; i < n_tmps; i++) - set[i] = False; - - /* start off by recording IRTemp uses in the next field. */ - addUses_Expr(set, bb->next); - - /* First pass */ + *i_unconditional_exit = -1; /* Work backwards through the stmts */ - i_unconditional_exit = -1; - for (i = bb->stmts_used-1; i >= 0; i--) { - st = bb->stmts[i]; + for (Int i = stmts->stmts_used - 1; i >= 0; i--) { + IRStmt* st = stmts->stmts[i]; if (st->tag == Ist_NoOp) continue; /* take note of any unconditional exits */ - if (st->tag == Ist_Exit - && isOneU1(st->Ist.Exit.guard)) - i_unconditional_exit = i; - if (st->tag == Ist_WrTmp - && set[(Int)(st->Ist.WrTmp.tmp)] == False) { + if (st->tag == Ist_Exit && isOneU1(st->Ist.Exit.guard)) + *i_unconditional_exit = i; + if (st->tag == Ist_WrTmp && set[st->Ist.WrTmp.tmp] == False) { /* it's an IRTemp which never got used. Delete it. */ if (DEBUG_IROPT) { vex_printf("DEAD: "); - ppIRStmt(st); + ppIRStmt(st, NULL, 0); vex_printf("\n"); } - bb->stmts[i] = IRStmt_NoOp(); + stmts->stmts[i] = IRStmt_NoOp(); } else if (st->tag == Ist_Dirty @@ -3136,30 +3297,44 @@ static Bool isOneU1 ( IRExpr* e ) && isZeroU1(st->Ist.Dirty.details->guard)) { /* This is a dirty helper which will never get called. Delete it. */ - bb->stmts[i] = IRStmt_NoOp(); + stmts->stmts[i] = IRStmt_NoOp(); } else { /* Note any IRTemp uses made by the current statement. */ addUses_Stmt(set, st); } } +} + +void do_deadcode_BB(IRSB* bb) +{ + Int i_unconditional_exit; + UInt n_tmps = bb->tyenv->used; + Bool* set = LibVEX_Alloc_inline(n_tmps * sizeof(Bool)); + for (UInt i = 0; i < n_tmps; i++) + set[i] = False; + + /* start off by recording IRTemp uses in the next field. */ + addUses_Expr(set, bb->next); + + /* First pass */ + do_deadcode_IRStmtVec(set, bb->stmts, &i_unconditional_exit); /* Optional second pass: if any unconditional exits were found, delete them and all following statements. */ - if (i_unconditional_exit != -1) { if (0) vex_printf("ZAPPING ALL FORWARDS from %d\n", i_unconditional_exit); vassert(i_unconditional_exit >= 0 - && i_unconditional_exit < bb->stmts_used); + && i_unconditional_exit < bb->stmts->stmts_used); bb->next - = IRExpr_Const( bb->stmts[i_unconditional_exit]->Ist.Exit.dst ); + = IRExpr_Const(bb->stmts->stmts[i_unconditional_exit]->Ist.Exit.dst); bb->jumpkind - = bb->stmts[i_unconditional_exit]->Ist.Exit.jk; + = bb->stmts->stmts[i_unconditional_exit]->Ist.Exit.jk; bb->offsIP - = bb->stmts[i_unconditional_exit]->Ist.Exit.offsIP; - for (i = i_unconditional_exit; i < bb->stmts_used; i++) - bb->stmts[i] = IRStmt_NoOp(); + = bb->stmts->stmts[i_unconditional_exit]->Ist.Exit.offsIP; + for (UInt i = i_unconditional_exit; i < bb->stmts->stmts_used; i++) + bb->stmts->stmts[i] = IRStmt_NoOp(); } } @@ -3169,19 +3344,22 @@ static Bool isOneU1 ( IRExpr* e ) /*--- collaboration with the front end ---*/ /*---------------------------------------------------------------*/ -static -IRSB* spec_helpers_BB( - IRSB* bb, - IRExpr* (*specHelper) (const HChar*, IRExpr**, IRStmt**, Int) - ) +static void spec_helpers_IRStmtVec( + IRStmtVec* stmts, + IRExpr* (*specHelper) (const HChar*, IRExpr**, IRStmt**, Int), + Bool* any) { - Int i; - IRStmt* st; IRExpr* ex; - Bool any = False; - for (i = bb->stmts_used-1; i >= 0; i--) { - st = bb->stmts[i]; + for (Int i = stmts->stmts_used - 1; i >= 0; i--) { + IRStmt* st = stmts->stmts[i]; + + if (st->tag == Ist_IfThenElse) { + IRIfThenElse* ite = st->Ist.IfThenElse.details; + spec_helpers_IRStmtVec(ite->then_leg, specHelper, any); + spec_helpers_IRStmtVec(ite->else_leg, specHelper, any); + continue; + } if (st->tag != Ist_WrTmp || st->Ist.WrTmp.data->tag != Iex_CCall) @@ -3189,15 +3367,14 @@ IRSB* spec_helpers_BB( ex = (*specHelper)( st->Ist.WrTmp.data->Iex.CCall.cee->name, st->Ist.WrTmp.data->Iex.CCall.args, - &bb->stmts[0], i ); + &stmts->stmts[0], i ); if (!ex) /* the front end can't think of a suitable replacement */ continue; - /* We got something better. Install it in the bb. */ - any = True; - bb->stmts[i] - = IRStmt_WrTmp(st->Ist.WrTmp.tmp, ex); + /* We got something better. Install it in stmts. */ + *any = True; + stmts->stmts[i] = IRStmt_WrTmp(st->Ist.WrTmp.tmp, ex); if (0) { vex_printf("SPEC: "); @@ -3207,10 +3384,22 @@ IRSB* spec_helpers_BB( vex_printf("\n"); } } +} - if (any) - bb = flatten_BB(bb); - return bb; +static +IRSB* spec_helpers_BB( + IRSB* bb, + IRExpr* (*specHelper) (const HChar*, IRExpr**, IRStmt**, Int) + ) +{ + Bool any = False; + spec_helpers_IRStmtVec(bb->stmts, specHelper, &any); + + if (any) { + return flatten_BB(bb); + } else { + return bb; + } } @@ -3863,18 +4052,10 @@ static AvailExpr* irExpr_to_AvailExpr ( IRExpr* e, Bool allowLoadsToBeCSEd ) return NULL; } - -/* The BB is modified in-place. Returns True if any changes were - made. The caller can choose whether or not loads should be CSEd. - In the normal course of things we don't do that, since CSEing loads - is something of a dodgy proposition if the guest program is doing - some screwy stuff to do with races and spinloops. */ - -static Bool do_cse_BB ( IRSB* bb, Bool allowLoadsToBeCSEd ) +static Bool do_cse_IRStmtVec(const IRTypeEnv* tyenv, IRStmtVec* stmts, + Bool allowLoadsToBeCSEd) { - Int i, j, paranoia; - IRTemp t, q; - IRStmt* st; + Int j, paranoia; AvailExpr* eprime; AvailExpr* ae; Bool invalidate; @@ -3883,10 +4064,6 @@ static Bool do_cse_BB ( IRSB* bb, Bool allowLoadsToBeCSEd ) HashHW* tenv = newHHW(); /* :: IRTemp -> IRTemp */ HashHW* aenv = newHHW(); /* :: AvailExpr* -> IRTemp */ - vassert(sizeof(IRTemp) <= sizeof(HWord)); - - if (0) { ppIRSB(bb); vex_printf("\n\n"); } - /* Iterate forwards over the stmts. On seeing "t = E", where E is one of the AvailExpr forms: let E' = apply tenv substitution to E @@ -3902,8 +4079,8 @@ static Bool do_cse_BB ( IRSB* bb, Bool allowLoadsToBeCSEd ) might invalidate some of the expressions in aenv. So there is an invalidate-bindings check for each statement seen. */ - for (i = 0; i < bb->stmts_used; i++) { - st = bb->stmts[i]; + for (UInt i = 0; i < stmts->stmts_used; i++) { + IRStmt* st = stmts->stmts[i]; /* ------ BEGIN invalidate aenv bindings ------ */ /* This is critical: remove from aenv any E' -> .. bindings @@ -3925,8 +4102,17 @@ static Bool do_cse_BB ( IRSB* bb, Bool allowLoadsToBeCSEd ) case Ist_NoOp: case Ist_IMark: case Ist_AbiHint: case Ist_WrTmp: case Ist_Exit: case Ist_LoadG: paranoia = 0; break; + case Ist_IfThenElse: { + IRIfThenElse* ite = st->Ist.IfThenElse.details; + anyDone |= do_cse_IRStmtVec(tyenv, ite->then_leg, + allowLoadsToBeCSEd); + anyDone |= do_cse_IRStmtVec(tyenv, ite->else_leg, + allowLoadsToBeCSEd); + paranoia = 0; + break; + } default: - vpanic("do_cse_BB(1)"); + vpanic("do_cse_IRStmtVec(1)"); } if (paranoia > 0) { @@ -3954,7 +4140,7 @@ static Bool do_cse_BB ( IRSB* bb, Bool allowLoadsToBeCSEd ) ae->u.GetIt.descr, IRExpr_RdTmp(ae->u.GetIt.ix), st->Ist.Put.offset, - typeOfIRExpr(bb->tyenv,st->Ist.Put.data) + typeOfIRExpr(tyenv, st->Ist.Put.data) ) != NoAlias) invalidate = True; } @@ -3972,7 +4158,7 @@ static Bool do_cse_BB ( IRSB* bb, Bool allowLoadsToBeCSEd ) invalidate = True; } else - vpanic("do_cse_BB(2)"); + vpanic("do_cse_IRStmtVec(2)"); } if (invalidate) { @@ -3988,7 +4174,7 @@ static Bool do_cse_BB ( IRSB* bb, Bool allowLoadsToBeCSEd ) if (st->tag != Ist_WrTmp) continue; - t = st->Ist.WrTmp.tmp; + IRTemp t = st->Ist.WrTmp.tmp; eprime = irExpr_to_AvailExpr(st->Ist.WrTmp.data, allowLoadsToBeCSEd); /* ignore if not of AvailExpr form */ if (!eprime) @@ -4008,18 +4194,34 @@ static Bool do_cse_BB ( IRSB* bb, Bool allowLoadsToBeCSEd ) /* A binding E' -> q was found. Replace stmt by "t = q" and note the t->q binding in tenv. */ /* (this is the core of the CSE action) */ - q = (IRTemp)aenv->val[j]; - bb->stmts[i] = IRStmt_WrTmp( t, IRExpr_RdTmp(q) ); - addToHHW( tenv, (HWord)t, (HWord)q ); + IRTemp q = (IRTemp) aenv->val[j]; + stmts->stmts[i] = IRStmt_WrTmp(t, IRExpr_RdTmp(q)); + addToHHW(tenv, (HWord) t, (HWord) q); anyDone = True; } else { /* No binding was found, so instead we add E' -> t to our collection of available expressions, replace this stmt with "t = E'", and move on. */ - bb->stmts[i] = IRStmt_WrTmp( t, availExpr_to_IRExpr(eprime) ); - addToHHW( aenv, (HWord)eprime, (HWord)t ); + stmts->stmts[i] = IRStmt_WrTmp(t, availExpr_to_IRExpr(eprime)); + addToHHW(aenv, (HWord) eprime, (HWord) t); } } + return anyDone; +} + +/* The BB is modified in-place. Returns True if any changes were + made. The caller can choose whether or not loads should be CSEd. + In the normal course of things we don't do that, since CSEing loads + is something of a dodgy proposition if the guest program is doing + some screwy stuff to do with races and spinloops. */ + +static Bool do_cse_BB ( IRSB* bb, Bool allowLoadsToBeCSEd ) +{ + vassert(sizeof(IRTemp) <= sizeof(HWord)); + + if (0) { ppIRSB(bb); vex_printf("\n\n"); } + + Bool anyDone = do_cse_IRStmtVec(bb->tyenv, bb->stmts, allowLoadsToBeCSEd); /* ppIRSB(bb); @@ -4062,13 +4264,11 @@ static Bool isAdd32OrSub32 ( IRExpr* e, IRTemp* tmp, Int* i32 ) other tmp2. Scan backwards from the specified start point -- an optimisation. */ -static Bool collapseChain ( IRSB* bb, Int startHere, - IRTemp tmp, - IRTemp* tmp2, Int* i32 ) +static Bool collapseChain(IRStmtVec* stmts, Int startHere, + IRTemp tmp, IRTemp* tmp2, Int* i32) { Int j, ii; IRTemp vv; - IRStmt* st; IRExpr* e; /* the (var, con) pair contain the current 'representation' for @@ -4079,7 +4279,7 @@ static Bool collapseChain ( IRSB* bb, Int startHere, /* Scan backwards to see if tmp can be replaced by some other tmp +/- a constant. */ for (j = startHere; j >= 0; j--) { - st = bb->stmts[j]; + IRStmt* st = stmts->stmts[j]; if (st->tag != Ist_WrTmp) continue; if (st->Ist.WrTmp.tmp != var) @@ -4106,14 +4306,13 @@ static Bool collapseChain ( IRSB* bb, Int startHere, /* ------- Main function for Add32/Sub32 chain collapsing ------ */ -static void collapse_AddSub_chains_BB ( IRSB* bb ) +static void collapse_AddSub_chains_IRStmtVec(IRStmtVec* stmts) { - IRStmt *st; IRTemp var, var2; - Int i, con, con2; + Int con, con2; - for (i = bb->stmts_used-1; i >= 0; i--) { - st = bb->stmts[i]; + for (Int i = stmts->stmts_used - 1; i >= 0; i--) { + IRStmt* st = stmts->stmts[i]; if (st->tag == Ist_NoOp) continue; @@ -4124,14 +4323,14 @@ static void collapse_AddSub_chains_BB ( IRSB* bb ) /* So e1 is of the form Add32(var,con) or Sub32(var,-con). Find out if var can be expressed as var2 + con2. */ - if (collapseChain(bb, i-1, var, &var2, &con2)) { + if (collapseChain(stmts, i - 1, var, &var2, &con2)) { if (DEBUG_IROPT) { vex_printf("replacing1 "); - ppIRStmt(st); + ppIRStmt(st, NULL, 0); vex_printf(" with "); } con2 += con; - bb->stmts[i] + stmts->stmts[i] = IRStmt_WrTmp( st->Ist.WrTmp.tmp, (con2 >= 0) @@ -4143,7 +4342,7 @@ static void collapse_AddSub_chains_BB ( IRSB* bb ) IRExpr_Const(IRConst_U32(-con2))) ); if (DEBUG_IROPT) { - ppIRStmt(bb->stmts[i]); + ppIRStmt(stmts->stmts[i], NULL, 0); vex_printf("\n"); } } @@ -4156,22 +4355,22 @@ static void collapse_AddSub_chains_BB ( IRSB* bb ) if (st->tag == Ist_WrTmp && st->Ist.WrTmp.data->tag == Iex_GetI && st->Ist.WrTmp.data->Iex.GetI.ix->tag == Iex_RdTmp - && collapseChain(bb, i-1, st->Ist.WrTmp.data->Iex.GetI.ix - ->Iex.RdTmp.tmp, &var2, &con2)) { + && collapseChain(stmts, i - 1, st->Ist.WrTmp.data->Iex.GetI.ix + ->Iex.RdTmp.tmp, &var2, &con2)) { if (DEBUG_IROPT) { vex_printf("replacing3 "); - ppIRStmt(st); + ppIRStmt(st, NULL, 0); vex_printf(" with "); } con2 += st->Ist.WrTmp.data->Iex.GetI.bias; - bb->stmts[i] + stmts->stmts[i] = IRStmt_WrTmp( st->Ist.WrTmp.tmp, IRExpr_GetI(st->Ist.WrTmp.data->Iex.GetI.descr, IRExpr_RdTmp(var2), con2)); if (DEBUG_IROPT) { - ppIRStmt(bb->stmts[i]); + ppIRStmt(stmts->stmts[i], NULL, 0); vex_printf("\n"); } continue; @@ -4181,29 +4380,39 @@ static void collapse_AddSub_chains_BB ( IRSB* bb ) IRPutI *puti = st->Ist.PutI.details; if (st->tag == Ist_PutI && puti->ix->tag == Iex_RdTmp - && collapseChain(bb, i-1, puti->ix->Iex.RdTmp.tmp, - &var2, &con2)) { + && collapseChain(stmts, i-1, puti->ix->Iex.RdTmp.tmp, + &var2, &con2)) { if (DEBUG_IROPT) { vex_printf("replacing2 "); - ppIRStmt(st); + ppIRStmt(st, NULL, 0); vex_printf(" with "); } con2 += puti->bias; - bb->stmts[i] + stmts->stmts[i] = IRStmt_PutI(mkIRPutI(puti->descr, IRExpr_RdTmp(var2), con2, puti->data)); if (DEBUG_IROPT) { - ppIRStmt(bb->stmts[i]); + ppIRStmt(stmts->stmts[i], NULL, 0); vex_printf("\n"); } continue; } + if (st->tag == Ist_IfThenElse) { + IRIfThenElse* ite = st->Ist.IfThenElse.details; + collapse_AddSub_chains_IRStmtVec(ite->then_leg); + collapse_AddSub_chains_IRStmtVec(ite->else_leg); + } } /* for */ } +static void collapse_AddSub_chains_BB ( IRSB* bb ) +{ + collapse_AddSub_chains_IRStmtVec(bb->stmts); +} + /*----------------------------------------... [truncated message content] |
|
From: Ivo R. <ir...@so...> - 2017-08-28 22:26:36
|
https://sourceware.org/git/gitweb.cgi?p=valgrind.git;h=448182d69df9dbe9a8cdad2019f6d85441f8cf0a commit 448182d69df9dbe9a8cdad2019f6d85441f8cf0a Author: Ivo Raisr <iv...@iv...> Date: Tue Aug 8 06:20:57 2017 +0200 Implement support for If-Then-Else and Phi nodes into ir_defs.c. Diff: --- VEX/priv/ir_defs.c | 1024 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 784 insertions(+), 240 deletions(-) diff --git a/VEX/priv/ir_defs.c b/VEX/priv/ir_defs.c index 8822800..f01eb40 100644 --- a/VEX/priv/ir_defs.c +++ b/VEX/priv/ir_defs.c @@ -33,6 +33,8 @@ without prior written permission. */ +/* Copyright (C) 2017-2017 Ivo Raisr <iv...@iv...> */ + #include "libvex_basictypes.h" #include "libvex_ir.h" #include "libvex.h" @@ -114,7 +116,7 @@ void ppIRTemp ( IRTemp tmp ) if (tmp == IRTemp_INVALID) vex_printf("IRTemp_INVALID"); else - vex_printf( "t%u", tmp); + vex_printf("t%u", tmp); } void ppIROp ( IROp op ) @@ -1571,8 +1573,73 @@ void ppIRMBusEvent ( IRMBusEvent event ) } } -void ppIRStmt ( const IRStmt* s ) +void ppIRPhi(const IRPhi* phi) +{ + ppIRTemp(phi->dst); + vex_printf(" = phi("); + ppIRTemp(phi->srcThen); + vex_printf(","); + ppIRTemp(phi->srcElse); + vex_printf(")"); +} + +static void print_depth(UInt depth) { + for (UInt i = 0; i < depth; i++) { + vex_printf(" "); + } +} + +void ppIRPhiVec(const IRPhiVec* phis, UInt depth) +{ + for (UInt i = 0; i < phis->phis_used; i++) { + print_depth(depth); + ppIRPhi(phis->phis[i]); + if (i < phis->phis_used - 1) { + vex_printf("\n"); + } + } +} + +void ppIRTempDefSet(const IRTempDefSet* defset, UInt depth) +{ + ppIRTypeEnvDefd(NULL, defset, depth); +} + +void ppIRIfThenElse_Hint(IRIfThenElse_Hint hint) { + switch (hint) { + case IfThenElse_ThenLikely: vex_printf("IfThenElse_ThenLikely"); break; + case IfThenElse_ElseLikely: vex_printf("IfThenElse_ElseLikely"); break; + default: vpanic("ppIRIfThenElse_Hint"); + } +} + +void ppIRIfThenElseCondHint(const IRIfThenElse* ite) +{ + vex_printf("if ("); + ppIRExpr(ite->cond); + vex_printf(") ["); + ppIRIfThenElse_Hint(ite->hint); + vex_printf("]"); +} + +void ppIRIfThenElse(const IRIfThenElse* ite, const IRTypeEnv* tyenv, UInt depth) +{ + ppIRIfThenElseCondHint(ite); + vex_printf(" then {\n"); + ppIRStmtVec(ite->then_leg, tyenv, depth + 1); + print_depth(depth); + vex_printf("} else {\n"); + ppIRStmtVec(ite->else_leg, tyenv, depth + 1); + print_depth(depth); + vex_printf("}\n"); + ppIRPhiVec(ite->phi_nodes, depth); +} + +void ppIRStmt(const IRStmt* s, const IRTypeEnv* tyenv, UInt depth) +{ + print_depth(depth); + if (!s) { vex_printf("!!! IRStmt* which is NULL !!!"); return; @@ -1653,41 +1720,83 @@ void ppIRStmt ( const IRStmt* s ) ppIRJumpKind(s->Ist.Exit.jk); vex_printf(" } "); break; - default: + case Ist_IfThenElse: + ppIRIfThenElse(s->Ist.IfThenElse.details, tyenv, depth); + break; + default: vpanic("ppIRStmt"); } } -void ppIRTypeEnv ( const IRTypeEnv* env ) +void ppIRTypeEnv(const IRTypeEnv* env) { - UInt i; - for (i = 0; i < env->types_used; i++) { + for (UInt i = 0; i < env->used; i++) { if (i % 8 == 0) - vex_printf( " "); + print_depth(1); ppIRTemp(i); - vex_printf( ":"); + vex_printf("[%u]:", env->ids[i]); ppIRType(env->types[i]); if (i % 8 == 7) vex_printf( "\n"); else vex_printf( " "); } - if (env->types_used > 0 && env->types_used % 8 != 7) + if (env->used > 0 && env->used % 8 != 7) vex_printf( "\n"); } -void ppIRSB ( const IRSB* bb ) +void ppIRTypeEnvDefd(const IRTypeEnv* tyenv, const IRTempDefSet* defset, + UInt depth) { - Int i; - vex_printf("IRSB {\n"); - ppIRTypeEnv(bb->tyenv); + UInt tmps_printed = 0; + + for (UInt slot = 0; slot < defset->slots_used; slot++) { + UChar slot_value = defset->set[slot]; + for (UInt bit = 0; bit < sizeof(UChar); bit++) { + if (slot_value & (1 << bit)) { + if (tmps_printed % 8 == 0) + print_depth(depth); + + IRTemp tmp = slot * sizeof(UChar) + bit; + ppIRTemp(tmp); + if (tyenv != NULL) { + vex_printf(":"); + ppIRType(tyenv->types[tmp]); + } + + if (tmps_printed % 8 == 7) { + vex_printf("\n"); + } else { + vex_printf(" "); + } + + tmps_printed += 1; + } + } + } + + if (tmps_printed > 0 && tmps_printed % 8 != 7) + vex_printf("\n"); +} + +void ppIRStmtVec(const IRStmtVec* stmts, const IRTypeEnv* tyenv, UInt depth) +{ + ppIRTypeEnvDefd(tyenv, stmts->defset, depth); vex_printf("\n"); - for (i = 0; i < bb->stmts_used; i++) { - vex_printf( " "); - ppIRStmt(bb->stmts[i]); - vex_printf( "\n"); + for (UInt i = 0; i < stmts->stmts_used; i++) { + ppIRStmt(stmts->stmts[i], tyenv, depth); + vex_printf("\n"); } - vex_printf( " PUT(%d) = ", bb->offsIP ); +} + +void ppIRSB ( const IRSB* bb ) +{ + UInt depth = 0; + + vex_printf("IRSB {\n"); + ppIRStmtVec(bb->stmts, bb->tyenv, depth + 1); + print_depth(depth + 1); + vex_printf("PUT(%d) = ", bb->offsIP); ppIRExpr( bb->next ); vex_printf( "; exit-"); ppIRJumpKind(bb->jumpkind); @@ -2132,6 +2241,49 @@ IRLoadG* mkIRLoadG ( IREndness end, IRLoadGOp cvt, return lg; } +/* Constructors -- IRIfThenElse */ + +IRPhi* mkIRPhi(IRTemp dst, IRTemp srcThen, IRTemp srcElse) +{ + IRPhi* phi = LibVEX_Alloc_inline(sizeof(IRPhi)); + phi->dst = dst; + phi->srcThen = srcThen; + phi->srcElse = srcElse; + return phi; +} + +IRPhiVec* emptyIRPhiVec(void) +{ + IRPhiVec* vec = LibVEX_Alloc_inline(sizeof(IRPhiVec)); + vec->phis_used = 0; + vec->phis_size = 8; + vec->phis = LibVEX_Alloc_inline(vec->phis_size * sizeof(IRPhi*)); + return vec; +} + +IRTempDefSet* emptyIRTempDefSet(void) +{ + IRTempDefSet* defset = LibVEX_Alloc_inline(sizeof(IRTempDefSet)); + defset->slots_used = 0; + defset->slots_size = 8 / sizeof(UChar); + vassert(defset->slots_size >= 1); + defset->set = LibVEX_Alloc_inline(defset->slots_size * sizeof(UChar)); + return defset; +} + +IRIfThenElse* mkIRIfThenElse(IRExpr* cond, IRIfThenElse_Hint hint, + IRStmtVec* then_leg, IRStmtVec* else_leg, + IRPhiVec* phi_nodes) +{ + IRIfThenElse* ite = LibVEX_Alloc_inline(sizeof(IRIfThenElse)); + ite->cond = cond; + ite->hint = hint; + ite->then_leg = then_leg; + ite->else_leg = else_leg; + ite->phi_nodes = phi_nodes; + return ite; +} + /* Constructors -- IRStmt */ @@ -2243,40 +2395,76 @@ IRStmt* IRStmt_Exit ( IRExpr* guard, IRJumpKind jk, IRConst* dst, return s; } +IRStmt* IRStmt_IfThenElse(IRExpr* cond, IRIfThenElse_Hint hint, + IRStmtVec* then_leg, IRStmtVec* else_leg, + IRPhiVec* phi_nodes) +{ + IRStmt* s = LibVEX_Alloc_inline(sizeof(IRStmt)); + s->tag = Ist_IfThenElse; + s->Ist.IfThenElse.details = mkIRIfThenElse(cond, hint, then_leg, else_leg, + phi_nodes); + return s; +} + /* Constructors -- IRTypeEnv */ -IRTypeEnv* emptyIRTypeEnv ( void ) +static IRTypeEnv* emptyIRTypeEnv ( void ) { IRTypeEnv* env = LibVEX_Alloc_inline(sizeof(IRTypeEnv)); env->types = LibVEX_Alloc_inline(8 * sizeof(IRType)); - env->types_size = 8; - env->types_used = 0; + env->ids = LibVEX_Alloc_inline(8 * sizeof(IRStmtVecID)); + env->size = 8; + env->used = 0; return env; } +/* Constructors -- IRStmtVec */ + +IRStmtVec* emptyIRStmtVec(void) +{ + IRStmtVec* stmts = LibVEX_Alloc_inline(sizeof(IRStmtVec)); + stmts->stmts_used = 0; + stmts->stmts_size = 8; + stmts->stmts = LibVEX_Alloc_inline(stmts->stmts_size * sizeof(IRStmt*)); + stmts->id = IRStmtVecID_INVALID; + stmts->parent = NULL; + stmts->defset = emptyIRTempDefSet(); + return stmts; +} + + /* Constructors -- IRSB */ IRSB* emptyIRSB ( void ) { - IRSB* bb = LibVEX_Alloc_inline(sizeof(IRSB)); - bb->tyenv = emptyIRTypeEnv(); - bb->stmts_used = 0; - bb->stmts_size = 8; - bb->stmts = LibVEX_Alloc_inline(bb->stmts_size * sizeof(IRStmt*)); - bb->next = NULL; - bb->jumpkind = Ijk_Boring; - bb->offsIP = 0; + IRSB* bb = LibVEX_Alloc_inline(sizeof(IRSB)); + bb->tyenv = emptyIRTypeEnv(); + bb->stmts = emptyIRStmtVec(); + bb->id_seq = 0; + bb->next = NULL; + bb->jumpkind = Ijk_Boring; + bb->offsIP = 0; + + bb->stmts->id = nextIRStmtVecID(bb); return bb; } +IRStmtVecID nextIRStmtVecID(IRSB* irsb) +{ + IRStmtVecID next = irsb->id_seq; + irsb->id_seq += 1; + vassert(irsb->id_seq != IRStmtVecID_INVALID); + return next; +} -/*---------------------------------------------------------------*/ -/*--- (Deep) copy constructors. These make complete copies ---*/ -/*--- the original, which can be modified without affecting ---*/ -/*--- the original. ---*/ -/*---------------------------------------------------------------*/ + +/*----------------------------------------------------------------*/ +/*--- (Deep) copy constructors. These make complete copies ---*/ +/*--- of the original, which can be modified without affecting ---*/ +/*--- the original. ---*/ +/*----------------------------------------------------------------*/ /* Copying IR Expr vectors (for call args). */ @@ -2441,7 +2629,35 @@ IRPutI* deepCopyIRPutI ( const IRPutI * puti ) deepCopyIRExpr(puti->data)); } -IRStmt* deepCopyIRStmt ( const IRStmt* s ) +IRPhi* deepCopyIRPhi(const IRPhi* phi) +{ + return mkIRPhi(phi->dst, phi->srcThen, phi->srcElse); +} + +IRPhiVec* deepCopyIRPhiVec(const IRPhiVec* vec) +{ + IRPhiVec* vec2 = LibVEX_Alloc_inline(sizeof(IRPhiVec)); + vec2->phis_used = vec2->phis_size = vec->phis_used; + IRPhi **phis2 = LibVEX_Alloc_inline(vec2->phis_used * sizeof(IRPhi*)); + for (UInt i = 0; i < vec2->phis_used; i++) + phis2[i] = deepCopyIRPhi(vec->phis[i]); + vec2->phis = phis2; + return vec2; +} + +IRTempDefSet* deepCopyIRTempDefSet(const IRTempDefSet* defset) +{ + IRTempDefSet* defset2 = LibVEX_Alloc_inline(sizeof(IRTempDefSet)); + defset2->slots_used = defset2->slots_size = defset->slots_used; + UChar* set2 = LibVEX_Alloc_inline(defset2->slots_used * sizeof(UChar)); + for (UInt i = 0; i < defset2->slots_used; i++) { + set2[i] = defset->set[i]; + } + defset2->set = set2; + return defset2; +} + +IRStmt* deepCopyIRStmt(const IRStmt* s, IRStmtVec* parent) { switch (s->tag) { case Ist_NoOp: @@ -2498,33 +2714,51 @@ IRStmt* deepCopyIRStmt ( const IRStmt* s ) s->Ist.Exit.jk, deepCopyIRConst(s->Ist.Exit.dst), s->Ist.Exit.offsIP); + case Ist_IfThenElse: { + const IRIfThenElse* ite = s->Ist.IfThenElse.details; + return IRStmt_IfThenElse(deepCopyIRExpr(ite->cond), ite->hint, + deepCopyIRStmtVec(ite->then_leg, parent), + deepCopyIRStmtVec(ite->else_leg, parent), + deepCopyIRPhiVec(ite->phi_nodes)); + } default: vpanic("deepCopyIRStmt"); } } -IRTypeEnv* deepCopyIRTypeEnv ( const IRTypeEnv* src ) +IRStmtVec* deepCopyIRStmtVec(const IRStmtVec* src, IRStmtVec* parent) +{ + IRStmtVec* vec2 = LibVEX_Alloc_inline(sizeof(IRStmtVec)); + vec2->id = src->id; + vec2->parent = parent; + vec2->defset = deepCopyIRTempDefSet(src->defset); + vec2->stmts_used = vec2->stmts_size = src->stmts_used; + IRStmt **stmts2 = LibVEX_Alloc_inline(vec2->stmts_used * sizeof(IRStmt*)); + for (UInt i = 0; i < vec2->stmts_used; i++) { + stmts2[i] = deepCopyIRStmt(src->stmts[i], vec2); + } + vec2->stmts = stmts2; + return vec2; +} + +IRTypeEnv* deepCopyIRTypeEnv(const IRTypeEnv* src) { - Int i; IRTypeEnv* dst = LibVEX_Alloc_inline(sizeof(IRTypeEnv)); - dst->types_size = src->types_size; - dst->types_used = src->types_used; - dst->types = LibVEX_Alloc_inline(dst->types_size * sizeof(IRType)); - for (i = 0; i < src->types_used; i++) + dst->size = src->size; + dst->used = src->used; + dst->types = LibVEX_Alloc_inline(dst->size * sizeof(IRType)); + dst->ids = LibVEX_Alloc_inline(dst->size * sizeof(IRStmtVecID)); + for (UInt i = 0; i < src->used; i++) { dst->types[i] = src->types[i]; + dst->ids[i] = src->ids[i]; + } return dst; } IRSB* deepCopyIRSB ( const IRSB* bb ) { - Int i; - IRStmt** sts2; IRSB* bb2 = deepCopyIRSBExceptStmts(bb); - bb2->stmts_used = bb2->stmts_size = bb->stmts_used; - sts2 = LibVEX_Alloc_inline(bb2->stmts_used * sizeof(IRStmt*)); - for (i = 0; i < bb2->stmts_used; i++) - sts2[i] = deepCopyIRStmt(bb->stmts[i]); - bb2->stmts = sts2; + bb2->stmts = deepCopyIRStmtVec(bb->stmts, NULL); return bb2; } @@ -2532,6 +2766,7 @@ IRSB* deepCopyIRSBExceptStmts ( const IRSB* bb ) { IRSB* bb2 = emptyIRSB(); bb2->tyenv = deepCopyIRTypeEnv(bb->tyenv); + bb2->id_seq = bb->id_seq; bb2->next = deepCopyIRExpr(bb->next); bb2->jumpkind = bb->jumpkind; bb2->offsIP = bb->offsIP; @@ -3550,53 +3785,147 @@ void typeOfPrimop ( IROp op, /*---------------------------------------------------------------*/ +/*--- Helper functions for the IR -- IR Phi Nodes ---*/ +/*---------------------------------------------------------------*/ + +void addIRPhiToIRPhiVec(IRPhiVec* phi_nodes, IRPhi* phi) +{ + if (phi_nodes->phis_used == phi_nodes->phis_size) { + IRPhi** phis2 + = LibVEX_Alloc_inline(2 * phi_nodes->phis_size * sizeof(IRPhi*)); + for (UInt i = 0; i < phi_nodes->phis_size; i++) + phis2[i] = phi_nodes->phis[i]; + phi_nodes->phis = phis2; + phi_nodes->phis_size *= 2; + } + + vassert(phi_nodes->phis_used < phi_nodes->phis_size); + phi_nodes->phis[phi_nodes->phis_used] = phi; + phi_nodes->phis_used += 1; +} + + +/*---------------------------------------------------------------*/ +/*--- Helper functions for the IR -- IR Temp Defined Set ---*/ +/*---------------------------------------------------------------*/ + +void setIRTempDefined(IRTempDefSet* defset, IRTemp tmp) +{ + UInt slots_required = (tmp + sizeof(UChar)) / sizeof(UChar); + + if (slots_required >= defset->slots_size) { + UInt new_size = (slots_required > 2 * defset->slots_size) ? + slots_required : 2 * defset->slots_size; + UChar* new_set = LibVEX_Alloc_inline(new_size * sizeof(UChar)); + for (UInt i = 0; i < defset->slots_used; i++) { + new_set[i] = defset->set[i]; + } + defset->set = new_set; + defset->slots_size = new_size; + } + + if (slots_required > defset->slots_used) { + for (UInt i = defset->slots_used; i < slots_required; i++) { + defset->set[i] = 0; + } + defset->slots_used = slots_required; + } + + vassert(!isIRTempDefined(defset, tmp)); + + UInt mask = (1 << (tmp % sizeof(UChar))); + defset->set[tmp / sizeof(UChar)] |= mask; +} + +void clearIRTempDefSet(IRTempDefSet* defset) +{ + for (UInt i = 0; i < defset->slots_used; i++) { + defset->set[i] = 0; + } +} + +/*---------------------------------------------------------------*/ /*--- Helper functions for the IR -- IR Basic Blocks ---*/ /*---------------------------------------------------------------*/ -void addStmtToIRSB ( IRSB* bb, IRStmt* st ) +void addStmtToIRStmtVec(IRStmtVec* stmts, IRStmt* st) { - Int i; - if (bb->stmts_used == bb->stmts_size) { - IRStmt** stmts2 = LibVEX_Alloc_inline(2 * bb->stmts_size * sizeof(IRStmt*)); - for (i = 0; i < bb->stmts_size; i++) - stmts2[i] = bb->stmts[i]; - bb->stmts = stmts2; - bb->stmts_size *= 2; + if (stmts->stmts_used == stmts->stmts_size) { + IRStmt** stmts2 + = LibVEX_Alloc_inline(2 * stmts->stmts_size * sizeof(IRStmt*)); + for (UInt i = 0; i < stmts->stmts_size; i++) + stmts2[i] = stmts->stmts[i]; + stmts->stmts = stmts2; + stmts->stmts_size *= 2; } - vassert(bb->stmts_used < bb->stmts_size); - bb->stmts[bb->stmts_used] = st; - bb->stmts_used++; + vassert(stmts->stmts_used < stmts->stmts_size); + stmts->stmts[stmts->stmts_used] = st; + stmts->stmts_used++; +} + +void addStmtToIRSB ( IRSB* bb, IRStmt* st ) +{ + addStmtToIRStmtVec(bb->stmts, st); } +IRStmt *addEmptyIfThenElse(IRSB* bb, IRStmtVec* parent, IRExpr* cond, + IRIfThenElse_Hint hint) +{ + IRStmtVec* then_leg = emptyIRStmtVec(); + then_leg->id = nextIRStmtVecID(bb); + then_leg->parent = parent; + + IRStmtVec* else_leg = emptyIRStmtVec(); + else_leg->id = nextIRStmtVecID(bb); + else_leg->parent = parent; + + IRStmt* st = IRStmt_IfThenElse(cond, hint, then_leg, else_leg, + emptyIRPhiVec()); + addStmtToIRStmtVec(parent, st); + return st; +} /*---------------------------------------------------------------*/ /*--- Helper functions for the IR -- IR Type Environments ---*/ /*---------------------------------------------------------------*/ -/* Allocate a new IRTemp, given its type. */ - -IRTemp newIRTemp ( IRTypeEnv* env, IRType ty ) +void ensureSpaceInIRTypeEnv(IRTypeEnv* env, UInt new_size) { - vassert(env); - vassert(env->types_used >= 0); - vassert(env->types_size >= 0); - vassert(env->types_used <= env->types_size); - if (env->types_used < env->types_size) { - env->types[env->types_used] = ty; - return env->types_used++; - } else { - Int i; - Int new_size = env->types_size==0 ? 8 : 2*env->types_size; - IRType* new_types - = LibVEX_Alloc_inline(new_size * sizeof(IRType)); - for (i = 0; i < env->types_used; i++) + vassert(env != NULL); + + if (new_size > env->size) { + IRType* new_types = LibVEX_Alloc_inline(new_size * sizeof(IRType)); + IRStmtVecID* new_ids = LibVEX_Alloc_inline(new_size * sizeof(IRStmtVecID)); + for (UInt i = 0; i < env->used; i++) { new_types[i] = env->types[i]; - env->types = new_types; - env->types_size = new_size; - return newIRTemp(env, ty); + new_ids[i] = env->ids[i]; + } + env->types = new_types; + env->ids = new_ids; + env->size = new_size; } } +IRTemp newIRTemp(IRTypeEnv* env, IRStmtVec* stmts, IRType ty) +{ + vassert(env != NULL); + vassert(stmts != NULL); + vassert(env->used >= 0); + vassert(env->size >= 0); + vassert(env->used <= env->size); + + if (env->used == env->size) { + ensureSpaceInIRTypeEnv(env, 2 * env->size); + } + + IRTemp tmp = env->used; + env->used += 1; + env->types[tmp] = ty; + env->ids[tmp] = stmts->id; + setIRTempDefined(stmts->defset, tmp); + return tmp; +} + /*---------------------------------------------------------------*/ /*--- Helper functions for the IR -- finding types of exprs ---*/ @@ -3606,7 +3935,7 @@ inline IRType typeOfIRTemp ( const IRTypeEnv* env, IRTemp tmp ) { vassert(tmp >= 0); - vassert(tmp < env->types_used); + vassert(tmp < env->used); return env->types[tmp]; } @@ -3736,6 +4065,9 @@ static inline Bool isIRAtom_or_VECRET_or_GSPTR ( const IRExpr* e ) return UNLIKELY(is_IRExpr_VECRET_or_GSPTR(e)); } +static Bool isFlatIRStmtVec(const IRStmtVec* stmts); + +static Bool isFlatIRStmt ( const IRStmt* st ) { Int i; @@ -3835,11 +4167,30 @@ Bool isFlatIRStmt ( const IRStmt* st ) return True; case Ist_Exit: return isIRAtom(st->Ist.Exit.guard); + case Ist_IfThenElse: + return isIRAtom(st->Ist.IfThenElse.details->cond) + && isFlatIRStmtVec(st->Ist.IfThenElse.details->then_leg) + && isFlatIRStmtVec(st->Ist.IfThenElse.details->else_leg); default: vpanic("isFlatIRStmt(st)"); } } +static +Bool isFlatIRStmtVec(const IRStmtVec* stmts) +{ + for (UInt i = 0; i < stmts->stmts_used; i++) { + if (!isFlatIRStmt(stmts->stmts[i])) + return False; + } + + return True; +} + +Bool isFlatIRSB(const IRSB* irsb) +{ + return isFlatIRStmtVec(irsb->stmts); +} /*---------------------------------------------------------------*/ /*--- Sanity checking ---*/ @@ -3852,6 +4203,10 @@ Bool isFlatIRStmt ( const IRStmt* st ) bit expression, depending on the guest's word size. Each temp is assigned only once, before its uses. + Each temp assigned and referenced is in scope. + + Phi functions refer to existing, already assigned temporaries from + [parent, then leg, else leg]. */ static inline Int countArgs ( IRExpr** args ) @@ -3870,7 +4225,7 @@ void sanityCheckFail ( const IRSB* bb, const IRStmt* stmt, const HChar* what ) ppIRSB(bb); if (stmt) { vex_printf("\nIN STATEMENT:\n\n"); - ppIRStmt(stmt); + ppIRStmt(stmt, bb->tyenv, 1); } vex_printf("\n\nERROR = %s\n\n", what ); vpanic("sanityCheckFail: exiting due to bad IR"); @@ -3910,29 +4265,62 @@ static Bool saneIRConst ( const IRConst* con ) } } -/* Traverse a Stmt/Expr, inspecting IRTemp uses. Report any out of - range ones. Report any which are read and for which the current - def_count is zero. */ +/* Traverse a Stmt/Expr, inspecting IRTemp uses. Report any out of range or out + of scope ones. Report any which are read and for which the current + def_count is zero. Report any which are assigned more than once or assigned + after being used. */ + +static Bool inRangeIRTemp(const IRTypeEnv* tyenv, IRTemp tmp) +{ + if (tmp >= 0 || tmp < tyenv->used) { + return True; + } + return False; +} + +static Bool inScopeIRTemp(const IRTypeEnv* tyenv, const IRStmtVec* stmts, + IRTemp tmp) +{ + IRStmtVecID id = tyenv->ids[tmp]; + vassert(id != IRStmtVecID_INVALID); + + while (!(isIRTempDefined(stmts->defset, tmp))) { + stmts = stmts->parent; + if (stmts == NULL) + return False; + } + + return True; +} static -void useBeforeDef_Temp ( const IRSB* bb, const IRStmt* stmt, IRTemp tmp, - Int* def_counts ) +void useBeforeDef_Temp(const IRSB* bb, const IRStmtVec* stmts, + const IRStmt* stmt, IRTemp tmp, UInt def_counts[]) { - if (tmp < 0 || tmp >= bb->tyenv->types_used) - sanityCheckFail(bb,stmt, "out of range Temp in IRExpr"); + const IRTypeEnv* tyenv = bb->tyenv; + + if (!inRangeIRTemp(tyenv, tmp)) + sanityCheckFail(bb, stmt, "out of range Temp in IRExpr"); + if (!inScopeIRTemp(tyenv, stmts, tmp)) + sanityCheckFail(bb, stmt, "out of scope Temp in IRExpr"); + if (def_counts[tmp] < 1) - sanityCheckFail(bb,stmt, "IRTemp use before def in IRExpr"); + sanityCheckFail(bb, stmt, "IRTemp use before def in IRExpr"); } static -void assignedOnce_Temp(const IRSB *bb, const IRStmt *stmt, IRTemp tmp, - Int *def_counts, UInt n_def_counts, - const HChar *err_msg_out_of_range, - const HChar *err_msg_assigned_more_than_once) +void assignedOnce_Temp(const IRSB* bb, const IRStmtVec* stmts, + const IRStmt* stmt, IRTemp tmp, UInt def_counts[], + const HChar* err_msg_out_of_range, + const HChar* err_msg_out_of_scope, + const HChar* err_msg_assigned_more_than_once) { - if (tmp < 0 || tmp >= n_def_counts) { + const IRTypeEnv* tyenv = bb->tyenv; + + if (!inRangeIRTemp(tyenv, tmp)) sanityCheckFail(bb, stmt, err_msg_out_of_range); - } + if (!inScopeIRTemp(tyenv, stmts, tmp)) + sanityCheckFail(bb, stmt, err_msg_out_of_scope); def_counts[tmp]++; if (def_counts[tmp] > 1) { @@ -3941,43 +4329,44 @@ void assignedOnce_Temp(const IRSB *bb, const IRStmt *stmt, IRTemp tmp, } static -void useBeforeDef_Expr ( const IRSB* bb, const IRStmt* stmt, - const IRExpr* expr, Int* def_counts ) +void useBeforeDef_Expr(const IRSB *bb, const IRStmtVec* stmts, + const IRStmt* stmt, const IRExpr* expr, + UInt def_counts[]) { Int i; switch (expr->tag) { case Iex_Get: break; case Iex_GetI: - useBeforeDef_Expr(bb,stmt,expr->Iex.GetI.ix,def_counts); + useBeforeDef_Expr(bb, stmts, stmt, expr->Iex.GetI.ix, def_counts); break; case Iex_RdTmp: - useBeforeDef_Temp(bb,stmt,expr->Iex.RdTmp.tmp,def_counts); + useBeforeDef_Temp(bb, stmts, stmt, expr->Iex.RdTmp.tmp, def_counts); break; case Iex_Qop: { const IRQop* qop = expr->Iex.Qop.details; - useBeforeDef_Expr(bb,stmt,qop->arg1,def_counts); - useBeforeDef_Expr(bb,stmt,qop->arg2,def_counts); - useBeforeDef_Expr(bb,stmt,qop->arg3,def_counts); - useBeforeDef_Expr(bb,stmt,qop->arg4,def_counts); + useBeforeDef_Expr(bb, stmts, stmt, qop->arg1, def_counts); + useBeforeDef_Expr(bb, stmts, stmt, qop->arg2, def_counts); + useBeforeDef_Expr(bb, stmts, stmt, qop->arg3, def_counts); + useBeforeDef_Expr(bb, stmts, stmt, qop->arg4, def_counts); break; } case Iex_Triop: { const IRTriop* triop = expr->Iex.Triop.details; - useBeforeDef_Expr(bb,stmt,triop->arg1,def_counts); - useBeforeDef_Expr(bb,stmt,triop->arg2,def_counts); - useBeforeDef_Expr(bb,stmt,triop->arg3,def_counts); + useBeforeDef_Expr(bb, stmts, stmt, triop->arg1, def_counts); + useBeforeDef_Expr(bb, stmts, stmt, triop->arg2, def_counts); + useBeforeDef_Expr(bb, stmts, stmt, triop->arg3, def_counts); break; } case Iex_Binop: - useBeforeDef_Expr(bb,stmt,expr->Iex.Binop.arg1,def_counts); - useBeforeDef_Expr(bb,stmt,expr->Iex.Binop.arg2,def_counts); + useBeforeDef_Expr(bb, stmts, stmt, expr->Iex.Binop.arg1, def_counts); + useBeforeDef_Expr(bb, stmts, stmt, expr->Iex.Binop.arg2, def_counts); break; case Iex_Unop: - useBeforeDef_Expr(bb,stmt,expr->Iex.Unop.arg,def_counts); + useBeforeDef_Expr(bb, stmts, stmt, expr->Iex.Unop.arg, def_counts); break; case Iex_Load: - useBeforeDef_Expr(bb,stmt,expr->Iex.Load.addr,def_counts); + useBeforeDef_Expr(bb, stmts, stmt, expr->Iex.Load.addr, def_counts); break; case Iex_Const: break; @@ -3988,24 +4377,55 @@ void useBeforeDef_Expr ( const IRSB* bb, const IRStmt* stmt, /* These aren't allowed in CCall lists. Let's detect and throw them out here, though, rather than segfaulting a bit later on. */ - sanityCheckFail(bb,stmt, "IRExprP__* value in CCall arg list"); + sanityCheckFail(bb,stmt, + "IRExprP__* value in CCall arg list"); } else { - useBeforeDef_Expr(bb,stmt,arg,def_counts); + useBeforeDef_Expr(bb, stmts, stmt, arg, def_counts); } } break; case Iex_ITE: - useBeforeDef_Expr(bb,stmt,expr->Iex.ITE.cond,def_counts); - useBeforeDef_Expr(bb,stmt,expr->Iex.ITE.iftrue,def_counts); - useBeforeDef_Expr(bb,stmt,expr->Iex.ITE.iffalse,def_counts); + useBeforeDef_Expr(bb, stmts, stmt, expr->Iex.ITE.cond, def_counts); + useBeforeDef_Expr(bb, stmts, stmt, expr->Iex.ITE.iftrue, def_counts); + useBeforeDef_Expr(bb, stmts, stmt, expr->Iex.ITE.iffalse, def_counts); break; default: vpanic("useBeforeDef_Expr"); } } +static void useBeforeDef_IRPhi(const IRSB* bb, const IRStmtVec* stmts, + const IRStmt* stmt, const IRPhi* phi, + UInt def_counts[]) +{ + vassert(stmt->tag == Ist_IfThenElse); + + IRStmtVec* then_leg = stmt->Ist.IfThenElse.details->then_leg; + IRStmtVec* else_leg = stmt->Ist.IfThenElse.details->else_leg; + + useBeforeDef_Temp(bb, then_leg, stmt, phi->srcThen, def_counts); + useBeforeDef_Temp(bb, else_leg, stmt, phi->srcElse, def_counts); + + /* Check also that referenced IRStmtVec's actually exist and belong to + "parent", "then", and "else", respectively. */ + const IRTypeEnv* tyenv = bb->tyenv; + if (tyenv->ids[phi->dst] != stmts->id) { + sanityCheckFail(bb, stmt, "Istmt.IfThenElse.Phi.dst does not " + "reference parent IRStmtVec"); + } + if (tyenv->ids[phi->srcThen] != then_leg->id) { + sanityCheckFail(bb, stmt, "Istmt.IfThenElse.Phi.srcThen does not " + "reference \"then\" IRStmtVec leg"); + } + if (tyenv->ids[phi->srcElse] != else_leg->id) { + sanityCheckFail(bb, stmt, "Istmt.IfThenElse.Phi.srcElse does not " + "reference \"else\" IRStmtVec leg"); + } +} + static -void useBeforeDef_Stmt ( const IRSB* bb, const IRStmt* stmt, Int* def_counts ) +void useBeforeDef_Stmt(const IRSB* bb, const IRStmtVec* stmts, + const IRStmt* stmt, UInt def_counts[]) { Int i; const IRDirty* d; @@ -4017,50 +4437,51 @@ void useBeforeDef_Stmt ( const IRSB* bb, const IRStmt* stmt, Int* def_counts ) case Ist_IMark: break; case Ist_AbiHint: - useBeforeDef_Expr(bb,stmt,stmt->Ist.AbiHint.base,def_counts); - useBeforeDef_Expr(bb,stmt,stmt->Ist.AbiHint.nia,def_counts); + useBeforeDef_Expr(bb, stmts, stmt, stmt->Ist.AbiHint.base, def_counts); + useBeforeDef_Expr(bb, stmts, stmt, stmt->Ist.AbiHint.nia, def_counts); break; case Ist_Put: - useBeforeDef_Expr(bb,stmt,stmt->Ist.Put.data,def_counts); + useBeforeDef_Expr(bb, stmts, stmt, stmt->Ist.Put.data, def_counts); break; case Ist_PutI: puti = stmt->Ist.PutI.details; - useBeforeDef_Expr(bb,stmt,puti->ix,def_counts); - useBeforeDef_Expr(bb,stmt,puti->data,def_counts); + useBeforeDef_Expr(bb, stmts, stmt, puti->ix, def_counts); + useBeforeDef_Expr(bb, stmts, stmt, puti->data, def_counts); break; case Ist_WrTmp: - useBeforeDef_Expr(bb,stmt,stmt->Ist.WrTmp.data,def_counts); + useBeforeDef_Expr(bb, stmts, stmt, stmt->Ist.WrTmp.data, def_counts); break; case Ist_Store: - useBeforeDef_Expr(bb,stmt,stmt->Ist.Store.addr,def_counts); - useBeforeDef_Expr(bb,stmt,stmt->Ist.Store.data,def_counts); + useBeforeDef_Expr(bb, stmts, stmt, stmt->Ist.Store.addr, def_counts); + useBeforeDef_Expr(bb, stmts, stmt, stmt->Ist.Store.data, def_counts); break; case Ist_StoreG: sg = stmt->Ist.StoreG.details; - useBeforeDef_Expr(bb,stmt,sg->addr,def_counts); - useBeforeDef_Expr(bb,stmt,sg->data,def_counts); - useBeforeDef_Expr(bb,stmt,sg->guard,def_counts); + useBeforeDef_Expr(bb, stmts, stmt, sg->addr, def_counts); + useBeforeDef_Expr(bb, stmts, stmt, sg->data, def_counts); + useBeforeDef_Expr(bb, stmts, stmt, sg->guard, def_counts); break; case Ist_LoadG: lg = stmt->Ist.LoadG.details; - useBeforeDef_Expr(bb,stmt,lg->addr,def_counts); - useBeforeDef_Expr(bb,stmt,lg->alt,def_counts); - useBeforeDef_Expr(bb,stmt,lg->guard,def_counts); + useBeforeDef_Expr(bb, stmts, stmt, lg->addr, def_counts); + useBeforeDef_Expr(bb, stmts, stmt, lg->alt, def_counts); + useBeforeDef_Expr(bb, stmts, stmt, lg->guard, def_counts); break; case Ist_CAS: cas = stmt->Ist.CAS.details; - useBeforeDef_Expr(bb,stmt,cas->addr,def_counts); + useBeforeDef_Expr(bb, stmts, stmt, cas->addr, def_counts); if (cas->expdHi) - useBeforeDef_Expr(bb,stmt,cas->expdHi,def_counts); - useBeforeDef_Expr(bb,stmt,cas->expdLo,def_counts); + useBeforeDef_Expr(bb, stmts, stmt, cas->expdHi, def_counts); + useBeforeDef_Expr(bb, stmts, stmt, cas->expdLo, def_counts); if (cas->dataHi) - useBeforeDef_Expr(bb,stmt,cas->dataHi,def_counts); - useBeforeDef_Expr(bb,stmt,cas->dataLo,def_counts); + useBeforeDef_Expr(bb, stmts, stmt, cas->dataHi, def_counts); + useBeforeDef_Expr(bb, stmts, stmt, cas->dataLo, def_counts); break; case Ist_LLSC: - useBeforeDef_Expr(bb,stmt,stmt->Ist.LLSC.addr,def_counts); + useBeforeDef_Expr(bb, stmts, stmt, stmt->Ist.LLSC.addr, def_counts); if (stmt->Ist.LLSC.storedata != NULL) - useBeforeDef_Expr(bb,stmt,stmt->Ist.LLSC.storedata,def_counts); + useBeforeDef_Expr(bb, stmts, stmt, stmt->Ist.LLSC.storedata, + def_counts); break; case Ist_Dirty: d = stmt->Ist.Dirty.details; @@ -4070,17 +4491,23 @@ void useBeforeDef_Stmt ( const IRSB* bb, const IRStmt* stmt, Int* def_counts ) /* This is ensured by isFlatIRStmt */ ; } else { - useBeforeDef_Expr(bb,stmt,arg,def_counts); + useBeforeDef_Expr(bb, stmts, stmt, arg, def_counts); } } if (d->mFx != Ifx_None) - useBeforeDef_Expr(bb,stmt,d->mAddr,def_counts); + useBeforeDef_Expr(bb, stmts, stmt, d->mAddr, def_counts); break; case Ist_NoOp: case Ist_MBE: break; case Ist_Exit: - useBeforeDef_Expr(bb,stmt,stmt->Ist.Exit.guard,def_counts); + useBeforeDef_Expr(bb, stmts, stmt, stmt->Ist.Exit.guard, def_counts); + break; + case Ist_IfThenElse: + useBeforeDef_Expr(bb, stmts, stmt, stmt->Ist.IfThenElse.details->cond, + def_counts); + /* Traversing into legs and phi nodes driven from + sanityCheckIRStmtVec(). */ break; default: vpanic("useBeforeDef_Stmt"); @@ -4088,48 +4515,59 @@ void useBeforeDef_Stmt ( const IRSB* bb, const IRStmt* stmt, Int* def_counts ) } static -void assignedOnce_Stmt(const IRSB *bb, const IRStmt *stmt, - Int *def_counts, UInt n_def_counts) +void assignedOnce_Stmt(const IRSB* bb, const IRStmtVec* stmts, + const IRStmt* stmt, UInt def_counts[]) { switch (stmt->tag) { case Ist_WrTmp: assignedOnce_Temp( - bb, stmt, stmt->Ist.WrTmp.tmp, def_counts, n_def_counts, + bb, stmts, stmt, stmt->Ist.WrTmp.tmp, def_counts, "IRStmt.Tmp: destination tmp is out of range", + "IRStmt.Tmp: destination tmp is out of scope", "IRStmt.Tmp: destination tmp is assigned more than once"); break; case Ist_LoadG: assignedOnce_Temp( - bb, stmt, stmt->Ist.LoadG.details->dst, def_counts, n_def_counts, + bb, stmts, stmt, stmt->Ist.LoadG.details->dst, def_counts, "IRStmt.LoadG: destination tmp is out of range", + "IRStmt.LoadG: destination tmp is out of scope", "IRStmt.LoadG: destination tmp is assigned more than once"); break; case Ist_Dirty: if (stmt->Ist.Dirty.details->tmp != IRTemp_INVALID) { assignedOnce_Temp( - bb, stmt, stmt->Ist.Dirty.details->tmp, def_counts, n_def_counts, + bb, stmts, stmt, stmt->Ist.Dirty.details->tmp, def_counts, "IRStmt.Dirty: destination tmp is out of range", + "IRStmt.Dirty: destination tmp is out of scope", "IRStmt.Dirty: destination tmp is assigned more than once"); } break; case Ist_CAS: if (stmt->Ist.CAS.details->oldHi != IRTemp_INVALID) { assignedOnce_Temp( - bb, stmt, stmt->Ist.CAS.details->oldHi, def_counts, n_def_counts, + bb, stmts, stmt, stmt->Ist.CAS.details->oldHi, def_counts, "IRStmt.CAS: destination tmpHi is out of range", + "IRStmt.CAS: destination tmpHi is out of scope", "IRStmt.CAS: destination tmpHi is assigned more than once"); } assignedOnce_Temp( - bb, stmt, stmt->Ist.CAS.details->oldLo, def_counts, n_def_counts, + bb, stmts, stmt, stmt->Ist.CAS.details->oldLo, def_counts, "IRStmt.CAS: destination tmpLo is out of range", + "IRStmt.CAS: destination tmpLo is out of scope", "IRStmt.CAS: destination tmpLo is assigned more than once"); break; case Ist_LLSC: assignedOnce_Temp( - bb, stmt, stmt->Ist.LLSC.result, def_counts, n_def_counts, + bb, stmts, stmt, stmt->Ist.LLSC.result, def_counts, "IRStmt.LLSC: destination tmp is out of range", + "IRStmt.LLSC: destination tmp is out of scope", "IRStmt.LLSC: destination tmp is assigned more than once"); break; + case Ist_IfThenElse: { + /* Traversing into legs and phi nodes driven from + sanityCheckIRStmtVec(). */ + break; + } // Ignore all other cases case Ist_NoOp: case Ist_IMark: case Ist_AbiHint: case Ist_Put: case Ist_PutI: case Ist_Store: case Ist_StoreG: case Ist_MBE: case Ist_Exit: @@ -4139,9 +4577,19 @@ void assignedOnce_Stmt(const IRSB *bb, const IRStmt *stmt, } } +static void assignedOnce_IRPhi(const IRSB* bb, const IRStmtVec* stmts, + const IRStmt* stmt, const IRPhi* phi, + UInt def_counts[]) +{ + assignedOnce_Temp(bb, stmts, stmt, phi->dst, def_counts, + "IRStmt.IfThenElse.Phi: destination tmp is out of range", + "IRStmt.IfThenElse.Phi: destination tmp is out of scope", + "IRStmt.IfThenElse: destination tmp is assigned more than once"); +} + static -void tcExpr ( const IRSB* bb, const IRStmt* stmt, const IRExpr* expr, - IRType gWordTy ) +void tcExpr(const IRSB* bb, const IRStmtVec* stmts, const IRStmt* stmt, + const IRExpr* expr, IRType gWordTy) { Int i; IRType t_dst, t_arg1, t_arg2, t_arg3, t_arg4; @@ -4151,8 +4599,8 @@ void tcExpr ( const IRSB* bb, const IRStmt* stmt, const IRExpr* expr, case Iex_RdTmp: break; case Iex_GetI: - tcExpr(bb,stmt, expr->Iex.GetI.ix, gWordTy ); - if (typeOfIRExpr(tyenv,expr->Iex.GetI.ix) != Ity_I32) + tcExpr(bb, stmts, stmt, expr->Iex.GetI.ix, gWordTy); + if (typeOfIRExpr(tyenv, expr->Iex.GetI.ix) != Ity_I32) sanityCheckFail(bb,stmt,"IRExpr.GetI.ix: not :: Ity_I32"); if (!saneIRRegArray(expr->Iex.GetI.descr)) sanityCheckFail(bb,stmt,"IRExpr.GetI.descr: invalid descr"); @@ -4160,10 +4608,10 @@ void tcExpr ( const IRSB* bb, const IRStmt* stmt, const IRExpr* expr, case Iex_Qop: { IRType ttarg1, ttarg2, ttarg3, ttarg4; const IRQop* qop = expr->Iex.Qop.details; - tcExpr(bb,stmt, qop->arg1, gWordTy ); - tcExpr(bb,stmt, qop->arg2, gWordTy ); - tcExpr(bb,stmt, qop->arg3, gWordTy ); - tcExpr(bb,stmt, qop->arg4, gWordTy ); + tcExpr(bb, stmts, stmt, qop->arg1, gWordTy); + tcExpr(bb, stmts, stmt, qop->arg2, gWordTy); + tcExpr(bb, stmts, stmt, qop->arg3, gWordTy); + tcExpr(bb, stmts, stmt, qop->arg4, gWordTy); typeOfPrimop(qop->op, &t_dst, &t_arg1, &t_arg2, &t_arg3, &t_arg4); if (t_arg1 == Ity_INVALID || t_arg2 == Ity_INVALID @@ -4212,9 +4660,9 @@ void tcExpr ( const IRSB* bb, const IRStmt* stmt, const IRExpr* expr, case Iex_Triop: { IRType ttarg1, ttarg2, ttarg3; const IRTriop *triop = expr->Iex.Triop.details; - tcExpr(bb,stmt, triop->arg1, gWordTy ); - tcExpr(bb,stmt, triop->arg2, gWordTy ); - tcExpr(bb,stmt, triop->arg3, gWordTy ); + tcExpr(bb, stmts, stmt, triop->arg1, gWordTy); + tcExpr(bb, stmts, stmt, triop->arg2, gWordTy); + tcExpr(bb, stmts, stmt, triop->arg3, gWordTy); typeOfPrimop(triop->op, &t_dst, &t_arg1, &t_arg2, &t_arg3, &t_arg4); if (t_arg1 == Ity_INVALID || t_arg2 == Ity_INVALID @@ -4256,8 +4704,8 @@ void tcExpr ( const IRSB* bb, const IRStmt* stmt, const IRExpr* expr, } case Iex_Binop: { IRType ttarg1, ttarg2; - tcExpr(bb,stmt, expr->Iex.Binop.arg1, gWordTy ); - tcExpr(bb,stmt, expr->Iex.Binop.arg2, gWordTy ); + tcExpr(bb, stmts, stmt, expr->Iex.Binop.arg1, gWordTy); + tcExpr(bb, stmts, stmt, expr->Iex.Binop.arg2, gWordTy); typeOfPrimop(expr->Iex.Binop.op, &t_dst, &t_arg1, &t_arg2, &t_arg3, &t_arg4); if (t_arg1 == Ity_INVALID || t_arg2 == Ity_INVALID @@ -4293,7 +4741,7 @@ void tcExpr ( const IRSB* bb, const IRStmt* stmt, const IRExpr* expr, break; } case Iex_Unop: - tcExpr(bb,stmt, expr->Iex.Unop.arg, gWordTy ); + tcExpr(bb, stmts, stmt, expr->Iex.Unop.arg, gWordTy); typeOfPrimop(expr->Iex.Unop.op, &t_dst, &t_arg1, &t_arg2, &t_arg3, &t_arg4); if (t_arg1 == Ity_INVALID || t_arg2 != Ity_INVALID @@ -4303,7 +4751,7 @@ void tcExpr ( const IRSB* bb, const IRStmt* stmt, const IRExpr* expr, sanityCheckFail(bb,stmt,"Iex.Unop: arg ty doesn't match op ty"); break; case Iex_Load: - tcExpr(bb,stmt, expr->Iex.Load.addr, gWordTy); + tcExpr(bb, stmts, stmt, expr->Iex.Load.addr, gWordTy); if (typeOfIRExpr(tyenv, expr->Iex.Load.addr) != gWordTy) sanityCheckFail(bb,stmt,"Iex.Load.addr: not :: guest word type"); if (expr->Iex.Load.end != Iend_LE && expr->Iex.Load.end != Iend_BE) @@ -4320,10 +4768,11 @@ void tcExpr ( const IRSB* bb, const IRStmt* stmt, const IRExpr* expr, IRExpr* arg = expr->Iex.CCall.args[i]; if (UNLIKELY(is_IRExpr_VECRET_or_GSPTR(arg))) sanityCheckFail(bb,stmt,"Iex.CCall.args: is VECRET/GSPTR"); - tcExpr(bb,stmt, arg, gWordTy); + tcExpr(bb, stmts, stmt, arg, gWordTy); } if (expr->Iex.CCall.retty == Ity_I1) - sanityCheckFail(bb,stmt,"Iex.CCall.retty: cannot return :: Ity_I1"); + sanityCheckFail(bb,stmt, + "Iex.CCall.retty: cannot return :: Ity_I1"); for (i = 0; expr->Iex.CCall.args[i]; i++) if (typeOfIRExpr(tyenv, expr->Iex.CCall.args[i]) == Ity_I1) sanityCheckFail(bb,stmt,"Iex.CCall.arg: arg :: Ity_I1"); @@ -4333,9 +4782,9 @@ void tcExpr ( const IRSB* bb, const IRStmt* stmt, const IRExpr* expr, sanityCheckFail(bb,stmt,"Iex.Const.con: invalid const"); break; case Iex_ITE: - tcExpr(bb,stmt, expr->Iex.ITE.cond, gWordTy); - tcExpr(bb,stmt, expr->Iex.ITE.iftrue, gWordTy); - tcExpr(bb,stmt, expr->Iex.ITE.iffalse, gWordTy); + tcExpr(bb, stmts, stmt, expr->Iex.ITE.cond, gWordTy); + tcExpr(bb, stmts, stmt, expr->Iex.ITE.iftrue, gWordTy); + tcExpr(bb, stmts, stmt, expr->Iex.ITE.iffalse, gWordTy); if (typeOfIRExpr(tyenv, expr->Iex.ITE.cond) != Ity_I1) sanityCheckFail(bb,stmt,"Iex.ITE.cond: cond :: Ity_I1"); if (typeOfIRExpr(tyenv, expr->Iex.ITE.iftrue) @@ -4347,11 +4796,27 @@ void tcExpr ( const IRSB* bb, const IRStmt* stmt, const IRExpr* expr, } } +static +void tcPhi(const IRSB* bb, const IRStmtVec* stmts, const IRStmt* stmt, + const IRPhi* phi) +{ + vassert(stmt->tag == Ist_IfThenElse); + const IRTypeEnv* tyenv = bb->tyenv; + + if (typeOfIRTemp(tyenv, phi->srcThen) != typeOfIRTemp(tyenv, phi->srcElse)) { + sanityCheckFail(bb, stmt, "IRStmt.IfThenElse.Phi: 'then' and 'else' " + "tmp do not match"); + } + if (typeOfIRTemp(tyenv, phi->dst) != typeOfIRTemp(tyenv, phi->srcThen)) { + sanityCheckFail(bb, stmt, "IRStmt.IfThenElse.Phi: 'dst' and 'then' " + "tmp do not match"); + } +} static -void tcStmt ( const IRSB* bb, const IRStmt* stmt, IRType gWordTy ) +void tcStmt(const IRSB* bb, const IRStmtVec* stmts, const IRStmt* stmt, + Bool require_flat, IRType gWordTy) { - Int i; IRType tyExpd, tyData; const IRTypeEnv* tyenv = bb->tyenv; switch (stmt->tag) { @@ -4372,35 +4837,35 @@ void tcStmt ( const IRSB* bb, const IRStmt* stmt, IRType gWordTy ) "not :: guest word type"); break; case Ist_Put: - tcExpr( bb, stmt, stmt->Ist.Put.data, gWordTy ); - if (typeOfIRExpr(tyenv,stmt->Ist.Put.data) == Ity_I1) + tcExpr(bb, stmts, stmt, stmt->Ist.Put.data, gWordTy); + if (typeOfIRExpr(tyenv, stmt->Ist.Put.data) == Ity_I1) sanityCheckFail(bb,stmt,"IRStmt.Put.data: cannot Put :: Ity_I1"); break; case Ist_PutI:{ const IRPutI* puti = stmt->Ist.PutI.details; - tcExpr( bb, stmt, puti->data, gWordTy ); - tcExpr( bb, stmt, puti->ix, gWordTy ); - if (typeOfIRExpr(tyenv,puti->data) == Ity_I1) - sanityCheckFail(bb,stmt,"IRStmt.PutI.data: cannot PutI :: Ity_I1"); - if (typeOfIRExpr(tyenv,puti->data) - != puti->descr->elemTy) + tcExpr(bb, stmts, stmt, puti->data, gWordTy); + tcExpr(bb, stmts, stmt, puti->ix, gWordTy); + if (typeOfIRExpr(tyenv, puti->data) == Ity_I1) + sanityCheckFail(bb,stmt, + "IRStmt.PutI.data: cannot PutI :: Ity_I1"); + if (typeOfIRExpr(tyenv, puti->data) != puti->descr->elemTy) sanityCheckFail(bb,stmt,"IRStmt.PutI.data: data ty != elem ty"); - if (typeOfIRExpr(tyenv,puti->ix) != Ity_I32) + if (typeOfIRExpr(tyenv, puti->ix) != Ity_I32) sanityCheckFail(bb,stmt,"IRStmt.PutI.ix: not :: Ity_I32"); if (!saneIRRegArray(puti->descr)) sanityCheckFail(bb,stmt,"IRStmt.PutI.descr: invalid descr"); break; } case Ist_WrTmp: - tcExpr( bb, stmt, stmt->Ist.WrTmp.data, gWordTy ); + tcExpr(bb, stmts, stmt, stmt->Ist.WrTmp.data, gWordTy); if (typeOfIRTemp(tyenv, stmt->Ist.WrTmp.tmp) != typeOfIRExpr(tyenv, stmt->Ist.WrTmp.data)) sanityCheckFail(bb,stmt, "IRStmt.Put.Tmp: tmp and expr do not match"); break; case Ist_Store: - tcExpr( bb, stmt, stmt->Ist.Store.addr, gWordTy ); - tcExpr( bb, stmt, stmt->Ist.Store.data, gWordTy ); + tcExpr(bb, stmts, stmt, stmt->Ist.Store.addr, gWordTy); + tcExpr(bb, stmts, stmt, stmt->Ist.Store.data, gWordTy); if (typeOfIRExpr(tyenv, stmt->Ist.Store.addr) != gWordTy) sanityCheckFail(bb,stmt, "IRStmt.Store.addr: not :: guest word type"); @@ -4412,9 +4877,9 @@ void tcStmt ( const IRSB* bb, const IRStmt* stmt, IRType gWordTy ) break; case Ist_StoreG: { const IRStoreG* sg = stmt->Ist.StoreG.details; - tcExpr( bb, stmt, sg->addr, gWordTy ); - tcExpr( bb, stmt, sg->data, gWordTy ); - tcExpr( bb, stmt, sg->guard, gWordTy ); + tcExpr(bb, stmts, stmt, sg->addr, gWordTy); + tcExpr(bb, stmts, stmt, sg->data, gWordTy); + tcExpr(bb, stmts, stmt, sg->guard, gWordTy); if (typeOfIRExpr(tyenv, sg->addr) != gWordTy) sanityCheckFail(bb,stmt,"IRStmtG...addr: not :: guest word type"); if (typeOfIRExpr(tyenv, sg->data) == Ity_I1) @@ -4427,9 +4892,9 @@ void tcStmt ( const IRSB* bb, const IRStmt* stmt, IRType gWordTy ) } case Ist_LoadG: { const IRLoadG* lg = stmt->Ist.LoadG.details; - tcExpr( bb, stmt, lg->addr, gWordTy ); - tcExpr( bb, stmt, lg->alt, gWordTy ); - tcExpr( bb, stmt, lg->guard, gWordTy ); + tcExpr(bb, stmts, stmt, lg->addr, gWordTy); + tcExpr(bb, stmts, stmt, lg->alt, gWordTy); + tcExpr(bb, stmts, stmt, lg->guard, gWordTy); if (typeOfIRExpr(tyenv, lg->guard) != Ity_I1) sanityCheckFail(bb,stmt,"IRStmt.LoadG.guard: not :: Ity_I1"); if (typeOfIRExpr(tyenv, lg->addr) != gWordTy) @@ -4446,12 +4911,12 @@ void tcStmt ( const IRSB* bb, const IRStmt* stmt, IRType gWordTy ) case Ist_CAS: { const IRCAS* cas = stmt->Ist.CAS.details; /* make sure it's definitely either a CAS or a DCAS */ - if (cas->oldHi == IRTemp_INVALID + if (cas->oldHi == IRTemp_INVALID && cas->expdHi == NULL && cas->dataHi == NULL) { /* fine; it's a single cas */ } else - if (cas->oldHi != IRTemp_INVALID + if (cas->oldHi != IRTemp_INVALID && cas->expdHi != NULL && cas->dataHi != NULL) { /* fine; it's a double cas */ } @@ -4460,7 +4925,7 @@ void tcStmt ( const IRSB* bb, const IRStmt* stmt, IRType gWordTy ) goto bad_cas; } /* check the address type */ - tcExpr( bb, stmt, cas->addr, gWordTy ); + tcExpr(bb, stmts, stmt, cas->addr, gWordTy); if (typeOfIRExpr(tyenv, cas->addr) != gWordTy) goto bad_cas; /* check types on the {old,expd,data}Lo components agree */ tyExpd = typeOfIRExpr(tyenv, cas->expdLo); @@ -4533,7 +4998,7 @@ void tcStmt ( const IRSB* bb, const IRStmt* stmt, IRType gWordTy ) } if (d->nFxState < 0 || d->nFxState > VEX_N_FXSTATE) goto bad_dirty; - for (i = 0; i < d->nFxState; i++) { + for (UInt i = 0; i < d->nFxState; i++) { if (d->fxState[i].fx == Ifx_None) goto bad_dirty; if (d->fxState[i].size <= 0) goto bad_dirty; if (d->fxState[i].nRepeats == 0) { @@ -4548,7 +5013,7 @@ void tcStmt ( const IRSB* bb, const IRStmt* stmt, IRType gWordTy ) } /* check guard */ if (d->guard == NULL) goto bad_dirty; - tcExpr( bb, stmt, d->guard, gWordTy ); + tcExpr(bb, stmts, stmt, d->guard, gWordTy); if (typeOfIRExpr(tyenv, d->guard) != Ity_I1) sanityCheckFail(bb,stmt,"IRStmt.Dirty.guard not :: Ity_I1"); /* check types, minimally */ @@ -4559,7 +5024,7 @@ void tcStmt ( const IRSB* bb, const IRStmt* stmt, IRType gWordTy ) sanityCheckFail(bb,stmt,"IRStmt.Dirty.dst :: Ity_I1"); } UInt nVECRETs = 0, nGSPTRs = 0; - for (i = 0; d->args[i] != NULL; i++) { + for (UInt i = 0; d->args[i] != NULL; i++) { if (i >= 32) sanityCheckFail(bb,stmt,"IRStmt.Dirty: > 32 args"); const IRExpr* arg = d->args[i]; @@ -4618,8 +5083,8 @@ void tcStmt ( const IRSB* bb, const IRStmt* stmt, IRType gWordTy ) } break; case Ist_Exit: - tcExpr( bb, stmt, stmt->Ist.Exit.guard, gWordTy ); - if (typeOfIRExpr(tyenv,stmt->Ist.Exit.guard) != Ity_I1) + tcExpr(bb, stmts, stmt, stmt->Ist.Exit.guard, gWordTy); + if (typeOfIRExpr(tyenv, stmt->Ist.Exit.guard) != Ity_I1) sanityCheckFail(bb,stmt,"IRStmt.Exit.guard: not :: Ity_I1"); if (!saneIRConst(stmt->Ist.Exit.dst)) sanityCheckFail(bb,stmt,"IRStmt.Exit.dst: bad dst"); @@ -4629,77 +5094,156 @@ void tcStmt ( const IRSB* bb, const IRStmt* stmt, IRType gWordTy ) if (stmt->Ist.Exit.offsIP < 16) sanityCheckFail(bb,stmt,"IRStmt.Exit.offsIP: too low"); break; + case Ist_IfThenElse: + tcExpr(bb, stmts, stmt, stmt->Ist.IfThenElse.details->cond, gWordTy); + if (typeOfIRExpr(tyenv, stmt->Ist.IfThenElse.details->cond) != Ity_I1) + sanityCheckFail(bb,stmt,"IRStmt.IfThenElse.cond: not :: Ity_I1"); + /* Traversing into legs and phi nodes driven from + sanityCheckIRStmtVec(). */ + break; default: vpanic("tcStmt"); } } -void sanityCheckIRSB ( const IRSB* bb, const HChar* caller, - Bool require_flat, IRType guest_word_size ) +static void sanityCheckIRPhiNodes(const IRSB* bb, const IRStmtVec* stmts, + const IRStmt* stmt, const IRPhiVec* phi_nodes, UInt def_counts[]) { - Int i; - Int n_temps = bb->tyenv->types_used; - Int* def_counts = LibVEX_Alloc_inline(n_temps * sizeof(Int)); + for (UInt i = 0; i < phi_nodes->phis_used; i++) { + const IRPhi* phi = phi_nodes->phis[i]; + useBeforeDef_IRPhi(bb, stmts, stmt, phi, def_counts); + assignedOnce_IRPhi(bb, stmts, stmt, phi, def_counts); + tcPhi(bb, stmts, stmt, phi); + } +} - if (0) - vex_printf("sanityCheck: %s\n", caller); +static +void sanityCheckIRStmtVec(const IRSB* bb, const IRStmtVec* stmts, + Bool require_flat, UInt def_counts[], + UInt id_counts[], UInt n_ids, IRType gWordTy) +{ + IRStmtVecID id = stmts->id; + if (id == IRStmtVecID_INVALID) { + vpanic("sanityCheckIRStmtVec: invalid IRStmtVec ID"); + } - vassert(guest_word_size == Ity_I32 - || guest_word_size == Ity_I64); + if (id >= n_ids) { + vex_printf("IRStmtVec's ID (%u) is larger than number of IRStmtVec's " + "(%u)\n", id, n_ids); + sanityCheckFail(bb, NULL, "IRStmtVec's ID larger than number of " + "IRStmtVec's"); + } - if (bb->stmts_used < 0 || bb->stmts_size < 8 - || bb->stmts_used > bb->stmts_size) - /* this BB is so strange we can't even print it */ - vpanic("sanityCheckIRSB: stmts array limits wierd"); + id_counts[id] += 1; + if (id_counts[id] > 1) { + sanityCheckFail(bb, NULL, "the same IRStmtVec ID used more than once"); + } - /* Ensure each temp has a plausible type. */ - for (i = 0; i < n_temps; i++) { - IRType ty = typeOfIRTemp(bb->tyenv,(IRTemp)i); - if (!isPlausibleIRType(ty)) { - vex_printf("Temp t%d declared with implausible type 0x%x\n", - i, (UInt)ty); - sanityCheckFail(bb,NULL,"Temp declared with implausible type"); - } + if (stmts->stmts_used < 0 || stmts->stmts_size < 8 + || stmts->stmts_used > stmts->stmts_size) { + /* this IRStmtVec is so strange we can't even print it */ + vpanic("sanityCheckIRStmtVec: stmts array limits wierd"); } - const IRStmt* stmt; + for (UInt i = 0; i < stmts->stmts_used; i++) { + const IRStmt *stmt = stmts->stmts[i]; + if (stmt == NULL) + sanityCheckFail(bb, stmt, "IRStmt: is NULL"); - /* Check for flatness, if required. */ - if (require_flat) { - for (i = 0; i < bb->stmts_used; i++) { - stmt = bb->stmts[i]; - if (!stmt) - sanityCheckFail(bb, stmt, "IRStmt: is NULL"); - if (!isFlatIRStmt(stmt)) + /* Check for flatness, if required. */ + if (require_flat) { + if (!isFlatIRStmt(stmt)) { sanityCheckFail(bb, stmt, "IRStmt: is not flat"); + } + } + + /* Count the defs of each temp. Only one def is allowed. + Also, check that each used temp has already been defd. */ + useBeforeDef_Stmt(bb, stmts, stmt, def_counts); + assignedOnce_Stmt(bb, stmts, stmt, def_counts); + tcStmt(bb, stmts, stmt, require_flat, gWordTy); + + if (stmt->tag == Ist_IfThenElse) { + const IRIfThenElse* ite = stmt->Ist.IfThenElse.details; + const IRStmtVec* then_leg = ite->then_leg; + const IRStmtVec* else_leg = ite->else_leg; + + if (then_leg->parent == NULL) { + sanityCheckFail(bb, stmt, "IfThenElse.then.parent is NULL"); + } + if (else_leg->parent == NULL) { + sanityCheckFail(bb, stmt, "IfThenElse.else.parent is NULL"); + } + if (then_leg->parent != stmts) { + sanityCheckFail(bb, stmt, "IfThenElse.then.parent does not point " + "to its parent"); + } + if (else_leg->parent != stmts) { + sanityCheckFail(bb, stmt, "IfThenElse.else.parent does not point " + "to its parent"); + } + + sanityCheckIRStmtVec(bb, then_leg, require_flat, def_counts, + id_counts, n_ids, gWordTy); + sanityCheckIRStmtVec(bb, else_leg, require_flat, def_counts, + id_counts, n_ids, gWordTy); + sanityCheckIRPhiNodes(bb, stmts, stmt, ite->phi_nodes, def_counts); } - if (!isIRAtom(bb->next)) - sanityCheckFail(bb, NULL, "bb->next is not an atom"); } +} - /* Count the defs of each temp. Only one def is allowed. - Also, check that each used temp has already been defd. */ +/* Sanity checks basic block of IR. + Also checks for IRTyEnvID uniqueness. */ +void sanityCheckIRSB(const IRSB* bb, const HChar* caller, Bool require_flat, + IRType gWordTy) +{ + UInt n_ids = bb->id_seq; + UInt *id_counts = LibVEX_Alloc_inline(n_ids * sizeof(UInt)); + for (UInt i = 0; i < n_ids; i++) { + id_counts[i] = 0; + } - for (i = 0; i < n_temps; i++) + const IRTypeEnv* tyenv = bb->tyenv; + UInt n_temps = tyenv->used; + UInt *def_counts = LibVEX_Alloc_inline(n_temps * sizeof(UInt)); + for (UInt i = 0; i < n_temps; i++) { def_counts[i] = 0; + } + + if (0) + vex_printf("sanityCheck: %s\n", caller); - for (i = 0; i < bb->stmts_used; i++) { - stmt = bb->stmts[i]; - /* Check any temps used by this statement. */ - useBeforeDef_Stmt(bb,stmt,def_counts); + vassert(gWordTy == Ity_I32 || gWordTy == Ity_I64); - /* Now make note of any temps defd by this statement. */ - assignedOnce_Stmt(bb, stmt, def_counts, n_temps); + /* Ensure each temp has a plausible type. */ + for (UInt i = 0; i < n_temps; i++) { + IRTemp temp = (IRTemp) i; + IRType ty = typeOfIRTemp(tyenv, temp); + if (!isPlausibleIRType(ty)) { + vex_printf("Temp "); + ppIRTemp(temp); + vex_printf(" declared with implausible type 0x%x\n", (UInt) ty); + sanityCheckFail(bb, NULL, "Temp declared with implausible type"); + } } - /* Typecheck everything. */ - for (i = 0; i < bb->stmts_used; i++) - tcStmt(bb, bb->stmts[i], guest_word_size); - if (typeOfIRExpr(bb->tyenv,bb->next) != guest_word_size) + sanityCheckIRStmtVec(bb, bb->stmts, require_flat, def_counts, id_counts, + n_ids, gWordTy); + + if (require_flat) { + if (!isIRAtom(bb->next)) { + sanityCheckFail(bb, NULL, "bb->next is not an atom"); + } + } + + /* Typecheck also next destination. */ + if (typeOfIRExpr(bb->tyenv, bb->next) != gWordTy) { sanityCheckFail(bb, NULL, "bb->next field has wrong type"); + } /* because it would intersect with host_EvC_* */ - if (bb->offsIP < 16) + if (bb->offsIP < 16) { sanityCheckFail(bb, NULL, "bb->offsIP: too low"); + } } /*---------------------------------------------------------------*/ |
|
From: Ivo R. <ir...@so...> - 2017-08-28 22:26:29
|
https://sourceware.org/git/gitweb.cgi?p=valgrind.git;h=d630ee98787f95c717f7c9ae646dcf3f019ec088 commit d630ee98787f95c717f7c9ae646dcf3f019ec088 Author: Ivo Raisr <iv...@iv...> Date: Tue Aug 8 06:15:58 2017 +0200 Introduce If-Then-Else concept and Phi nodes into VEX IR. Diff: --- VEX/pub/libvex_ir.h | 799 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 516 insertions(+), 283 deletions(-) diff --git a/VEX/pub/libvex_ir.h b/VEX/pub/libvex_ir.h index 57fa9b6..af56186 100644 --- a/VEX/pub/libvex_ir.h +++ b/VEX/pub/libvex_ir.h @@ -56,13 +56,15 @@ Each IRSB contains three things: - a type environment, which indicates the type of each temporary value present in the IRSB - - a list of statements, which represent code - - a jump that exits from the end the IRSB - Because the blocks are multiple-exit, there can be additional - conditional exit statements that cause control to leave the IRSB - before the final exit. Also because of this, IRSBs can cover - multiple non-consecutive sequences of code (up to 3). These are - recorded in the type VexGuestExtents (see libvex.h). + - a vector of statements, which represent code + - a jump that exits from the end of the IRSB + Flow control can leave the IRSB before the final exit only in a leg of an + "if-then-else" statement. A leg of an "if-then-else" statement is just + another vector of statements. + "If-then-else" statements can be nested, however this is currently not + supported. + IRSBs can cover multiple non-consecutive sequences of code (up to 3). + These are recorded in the type VexGuestExtents (see libvex.h). Statements and expressions ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -397,7 +399,7 @@ typedef UInt IRTemp; /* Pretty-print an IRTemp. */ extern void ppIRTemp ( IRTemp ); -#define IRTemp_INVALID ((IRTemp)0xFFFFFFFF) +#define IRTemp_INVALID ((IRTemp) 0xFFFFFFFF) /* --------------- Primops (arity 1,2,3 and 4) --------------- */ @@ -2693,9 +2695,188 @@ extern IRLoadG* mkIRLoadG ( IREndness end, IRLoadGOp cvt, IRTemp dst, IRExpr* addr, IRExpr* alt, IRExpr* guard ); +/* ------------------ Phi Nodes ------------------ */ + +/* Assigns result of phi(src1, src2) to a temporary. + A "phi" function is special "phony" function that does not have its + corresponding machine operation. Let's consider this example: + if (cond) { + t1 = 0x3:I64 + } else { + t1 = 0x4:I64 + } + However this is not possible under SSA rules (a temporary cannot be assigned + more than once). Now the "phi" function comes handy: + if (cond) { + t2 = 0x3:I64 + } else { + t3 = 0x4:I64 + } + t1 = phi(t2,t3) +*/ +typedef + struct { + IRTemp dst; + IRTemp srcThen; + IRTemp srcElse; + } + IRPhi; + +typedef + struct { + IRPhi** phis; + UInt phis_size; + UInt phis_used; + } + IRPhiVec; + +extern void ppIRPhi(const IRPhi*); +extern IRPhi* mkIRPhi(IRTemp dst, IRTemp srcThen, IRTemp srcElse); +extern IRPhi* deepCopyIRPhi(const IRPhi*); +extern void ppIRPhiVec(const IRPhiVec*, UInt depth); +extern IRPhiVec* emptyIRPhiVec(void); +extern IRPhiVec* deepCopyIRPhiVec(const IRPhiVec*); + +extern void addIRPhiToIRPhiVec(IRPhiVec* , IRPhi*); + +/* ----------------- IRTemp Defined Set ----------------- */ + +/* An IRTemp is defined in an IRStmtVec. By keeping track of where every IRTemp + is defined, it is possible to reason about IRTemp's scope. + + Let's have this IRStmtVec hierarchy: + IRSB + |- IRStmtVec #0 + |- IRStmtVec #1 + |- IRStmtVec #2 + |- IRStmtVec #3 + So an IRTemp defined in IRStmtVec #2 is valid (in scope) only + in IRStmtVec's #2 and #3; and out of scope in IRStmtVec's #0 and #1. + + Every IRStmtVec has its IRTempDefSet structure. This is a bit set indexed + by IRTemp; value 1 means that a particular IRTemp is defined there. + + 'bits' array does not grow each time an IRTemp is added to IRSB; it grows + lazily only when an IRTemp is marked as defined in a particular IRStmtVec. + */ +typedef + struct { + UChar* set; // a bit set, use isIRTempDefined() for access + UInt slots_used; + UInt slots_size; + } + IRTempDefSet; + +static inline Bool isIRTempDefined(const IRTempDefSet* defset, IRTemp tmp) +{ + if ((tmp / sizeof(UChar)) < defset->slots_size) { + UInt mask = (1 << (tmp % sizeof(UChar))); + return toBool(defset->set[tmp / sizeof(UChar)] & mask); + } + return False; +} + +extern void setIRTempDefined(IRTempDefSet* defset, IRTemp tmp); +extern void clearIRTempDefSet(IRTempDefSet* defset); +extern void ppIRTempDefSet(const IRTempDefSet* defset, UInt depth); +extern IRTempDefSet* emptyIRTempDefSet(void); +extern IRTempDefSet* deepCopyIRTempDefSet(const IRTempDefSet* defset); + + +/* --------------- If-Then-Else control flow diamond --------------- */ + +/* If-Then-Else control flow diamond. It contains: + - Guard controling whether "then" or "else" leg is taken + - A hint which leg is more likely to be taken (hot path vs cold path) + - "then" and "else" legs with vectors of statements + At the moment, nested "if-then-else" statements are not supported. + - Phi nodes, which are used to merge temporaries from "then" and "else" legs + + A leg can either end with an unconditional exit or join the main flow. + At the moment, unconditional exits are not supported. +*/ + +typedef + enum { + IfThenElse_ThenLikely=0x1E00, + IfThenElse_ElseLikely + } + IRIfThenElse_Hint; + +typedef + struct _IRStmtVec + IRStmtVec; + +typedef + struct _IRTypeEnv + IRTypeEnv; + +typedef + struct { + IRExpr* cond; + IRIfThenElse_Hint hint; + IRStmtVec* then_leg; + IRStmtVec* else_leg; + IRPhiVec* phi_nodes; + } + IRIfThenElse; + +extern void ppIRIfThenElse_Hint(IRIfThenElse_Hint hint); +/* Pretty print only If-Then-Else preamble: condition and hint. Not the legs. */ +extern void ppIRIfThenElseCondHint(const IRIfThenElse* ite); +extern void ppIRIfThenElse(const IRIfThenElse* ite, const IRTypeEnv* tyenv, + UInt depth); +extern IRIfThenElse* mkIRIfThenElse(IRExpr* cond, IRIfThenElse_Hint hint, + IRStmtVec* then_leg, IRStmtVec* else_leg, + IRPhiVec* phi_nodes); + /* ------------------ Statements ------------------ */ +/* IRStmt and IRStmtVec are mutually recursive. */ +typedef + struct _IRStmt + IRStmt; + +/* Uniquely identifies IRStmtVec in an IRSB, no matter how deeply nested. */ +typedef UShort IRStmtVecID; + +#define IRStmtVecID_INVALID ((IRStmtVecID) 0xFFFF) + +/* Vector of statements which contains: + - Statements themselves + - A unique IRStmtVecID + - Parent, which points to the parent IRStmtVec. Because "if-then-else" + statements cannot be currently nested, the parent is either NULL or points + to IRStmtVec #0. + - A set which keeps track of which IRTemp's are defined in this IRStmtVec. +*/ +struct _IRStmtVec { + IRStmt** stmts; + UInt stmts_size; + UInt stmts_used; + IRStmtVecID id; + IRStmtVec* parent; + IRTempDefSet* defset; +}; + +/* Pretty-prints a vector of statements. If 'tyenv' is not NULL, pretty-prints + IRStmtVec's defset using nicer ppIRTypeEnvDefd(). */ +extern void ppIRStmtVec(const IRStmtVec* stmts, const IRTypeEnv* tyenv, + UInt depth); + +/* Allocates an empty IRStmtVec with an invalid IRStmtVecID. + Such an IRStmtVec needs to have a valid IRStmtVecID - get it from + nextIRStmtVecID(). Only after this is done, then such an IRStmtVec is ready + for newIRTemp() to give out new temporaries. + Nested IRStmtVec also needs to have correctly set its parent. + + Function addEmptyIfThenElse() can be used conveniently instead. */ +extern IRStmtVec* emptyIRStmtVec(void); + +extern IRStmtVec* deepCopyIRStmtVec(const IRStmtVec* src, IRStmtVec* parent); + + /* The different kinds of statements. Their meaning is explained below in the comments for IRStmt. @@ -2708,7 +2889,7 @@ extern IRLoadG* mkIRLoadG ( IREndness end, IRLoadGOp cvt, typedef enum { - Ist_NoOp=0x1E00, + Ist_NoOp=0x1F00, Ist_IMark, /* META */ Ist_AbiHint, /* META */ Ist_Put, @@ -2721,8 +2902,9 @@ typedef Ist_LLSC, Ist_Dirty, Ist_MBE, - Ist_Exit - } + Ist_Exit, + Ist_IfThenElse + } IRStmtTag; /* A statement. Stored as a tagged union. 'tag' indicates what kind @@ -2734,237 +2916,264 @@ typedef For each kind of statement, we show what it looks like when pretty-printed with ppIRStmt(). */ -typedef - struct _IRStmt { - IRStmtTag tag; - union { - /* A no-op (usually resulting from IR optimisation). Can be - omitted without any effect. - - ppIRStmt output: IR-NoOp - */ - struct { - } NoOp; - - /* META: instruction mark. Marks the start of the statements - that represent a single machine instruction (the end of - those statements is marked by the next IMark or the end of - the IRSB). Contains the address and length of the - instruction. - - It also contains a delta value. The delta must be - subtracted from a guest program counter value before - attempting to establish, by comparison with the address - and length values, whether or not that program counter - value refers to this instruction. For x86, amd64, ppc32, - ppc64 and arm, the delta value is zero. For Thumb - instructions, the delta value is one. This is because, on - Thumb, guest PC values (guest_R15T) are encoded using the - top 31 bits of the instruction address and a 1 in the lsb; - hence they appear to be (numerically) 1 past the start of - the instruction they refer to. IOW, guest_R15T on ARM - holds a standard ARM interworking address. - - ppIRStmt output: ------ IMark(<addr>, <len>, <delta>) ------, - eg. ------ IMark(0x4000792, 5, 0) ------, - */ - struct { - Addr addr; /* instruction address */ - UInt len; /* instruction length */ - UChar delta; /* addr = program counter as encoded in guest state - - delta */ - } IMark; - - /* META: An ABI hint, which says something about this - platform's ABI. - - At the moment, the only AbiHint is one which indicates - that a given chunk of address space, [base .. base+len-1], - has become undefined. This is used on amd64-linux and - some ppc variants to pass stack-redzoning hints to whoever - wants to see them. It also indicates the address of the - next (dynamic) instruction that will be executed. This is - to help Memcheck to origin tracking. - - ppIRStmt output: ====== AbiHint(<base>, <len>, <nia>) ====== - eg. ====== AbiHint(t1, 16, t2) ====== - */ - struct { - IRExpr* base; /* Start of undefined chunk */ - Int len; /* Length of undefined chunk */ - IRExpr* nia; /* Address of next (guest) insn */ - } AbiHint; - - /* Write a guest register, at a fixed offset in the guest state. - ppIRStmt output: PUT(<offset>) = <data>, eg. PUT(60) = t1 - */ - struct { - Int offset; /* Offset into the guest state */ - IRExpr* data; /* The value to write */ - } Put; - - /* Write a guest register, at a non-fixed offset in the guest - state. See the comment for GetI expressions for more - information. - - ppIRStmt output: PUTI<descr>[<ix>,<bias>] = <data>, - eg. PUTI(64:8xF64)[t5,0] = t1 - */ - struct { - IRPutI* details; - } PutI; - - /* Assign a value to a temporary. Note that SSA rules require - each tmp is only assigned to once. IR sanity checking will - reject any block containing a temporary which is not assigned - to exactly once. - - ppIRStmt output: t<tmp> = <data>, eg. t1 = 3 - */ - struct { - IRTemp tmp; /* Temporary (LHS of assignment) */ - IRExpr* data; /* Expression (RHS of assignment) */ - } WrTmp; - - /* Write a value to memory. This is a normal store, not a - Store-Conditional. To represent a Store-Conditional, - instead use IRStmt.LLSC. - ppIRStmt output: ST<end>(<addr>) = <data>, eg. STle(t1) = t2 - */ - struct { - IREndness end; /* Endianness of the store */ - IRExpr* addr; /* store address */ - IRExpr* data; /* value to write */ - } Store; - - /* Guarded store. Note that this is defined to evaluate all - expression fields (addr, data) even if the guard evaluates - to false. - ppIRStmt output: - if (<guard>) ST<end>(<addr>) = <data> */ - struct { - IRStoreG* details; - } StoreG; - - /* Guarded load. Note that this is defined to evaluate all - expression fields (addr, alt) even if the guard evaluates - to false. - ppIRStmt output: - t<tmp> = if (<guard>) <cvt>(LD<end>(<addr>)) else <alt> */ - struct { - IRLoadG* details; - } LoadG; - - /* Do an atomic compare-and-swap operation. Semantics are - described above on a comment at the definition of IRCAS. - - ppIRStmt output: - t<tmp> = CAS<end>(<addr> :: <expected> -> <new>) - eg - t1 = CASle(t2 :: t3->Add32(t3,1)) - which denotes a 32-bit atomic increment - of a value at address t2 - - A double-element CAS may also be denoted, in which case <tmp>, - <expected> and <new> are all pairs of items, separated by - commas. - */ - struct { - IRCAS* details; - } CAS; - - /* Either Load-Linked or Store-Conditional, depending on - STOREDATA. - - If STOREDATA is NULL then this is a Load-Linked, meaning - that data is loaded from memory as normal, but a - 'reservation' for the address is also lodged in the - hardware. - - result = Load-Linked(addr, end) - - The data transfer type is the type of RESULT (I32, I64, - etc). ppIRStmt output: - - result = LD<end>-Linked(<addr>), eg. LDbe-Linked(t1) - - If STOREDATA is not NULL then this is a Store-Conditional, - hence: - - result = Store-Conditional(addr, storedata, end) - - The data transfer type is the type of STOREDATA and RESULT - has type Ity_I1. The store may fail or succeed depending - on the state of a previously lodged reservation on this - address. RESULT is written 1 if the store succeeds and 0 - if it fails. eg ppIRStmt output: - - result = ( ST<end>-Cond(<addr>) = <storedata> ) - eg t3 = ( STbe-Cond(t1, t2) ) - - In all cases, the address must be naturally aligned for - the transfer type -- any misaligned addresses should be - caught by a dominating IR check and side exit. This - alignment restriction exists because on at least some - LL/SC platforms (ppc), stwcx. etc will trap w/ SIGBUS on - misaligned addresses, and we have to actually generate - stwcx. on the host, and we don't want it trapping on the - host. - - Summary of rules for transfer type: - STOREDATA == NULL (LL): - transfer type = type of RESULT - STOREDATA != NULL (SC): - transfer type = type of STOREDATA, and RESULT :: Ity_I1 - */ - struct { - IREndness end; - IRTemp result; - IRExpr* addr; - IRExpr* storedata; /* NULL => LL, non-NULL => SC */ - } LLSC; - - /* Call (possibly conditionally) a C function that has side - effects (ie. is "dirty"). See the comments above the - IRDirty type declaration for more information. - - ppIRStmt output: - t<tmp> = DIRTY <guard> <effects> - ::: <callee>(<args>) - eg. - t1 = DIRTY t27 RdFX-gst(16,4) RdFX-gst(60,4) - ::: foo{0x380035f4}(t2) - */ - struct { - IRDirty* details; - } Dirty; - - /* A memory bus event - a fence, or acquisition/release of the - hardware bus lock. IR optimisation treats all these as fences - across which no memory references may be moved. - ppIRStmt output: MBusEvent-Fence, - MBusEvent-BusLock, MBusEvent-BusUnlock. - */ - struct { - IRMBusEvent event; - } MBE; - - /* Conditional exit from the middle of an IRSB. - ppIRStmt output: if (<guard>) goto {<jk>} <dst> - eg. if (t69) goto {Boring} 0x4000AAA:I32 - If <guard> is true, the guest state is also updated by - PUT-ing <dst> at <offsIP>. This is done because a - taken exit must update the guest program counter. - */ - struct { - IRExpr* guard; /* Conditional expression */ - IRConst* dst; /* Jump target (constant only) */ - IRJumpKind jk; /* Jump kind */ - Int offsIP; /* Guest state offset for IP */ - } Exit; - } Ist; - } - IRStmt; +struct _IRStmt { + IRStmtTag tag; + union { + /* A no-op (usually resulting from IR optimisation). Can be + omitted without any effect. + + ppIRStmt output: IR-NoOp + */ + struct { + } NoOp; + + /* META: instruction mark. Marks the start of the statements + that represent a single machine instruction (the end of + those statements is marked by the next IMark or the end of + the IRSB). Contains the address and length of the + instruction. + + It also contains a delta value. The delta must be + subtracted from a guest program counter value before + attempting to establish, by comparison with the address + and length values, whether or not that program counter + value refers to this instruction. For x86, amd64, ppc32, + ppc64 and arm, the delta value is zero. For Thumb + instructions, the delta value is one. This is because, on + Thumb, guest PC values (guest_R15T) are encoded using the + top 31 bits of the instruction address and a 1 in the lsb; + hence they appear to be (numerically) 1 past the start of + the instruction they refer to. IOW, guest_R15T on ARM + holds a standard ARM interworking address. + + ppIRStmt output: ------ IMark(<addr>, <len>, <delta>) ------, + eg. ------ IMark(0x4000792, 5, 0) ------, + */ + struct { + Addr addr; /* instruction address */ + UInt len; /* instruction length */ + UChar delta; /* addr = program counter as encoded in guest state + - delta */ + } IMark; + + /* META: An ABI hint, which says something about this + platform's ABI. + + At the moment, the only AbiHint is one which indicates + that a given chunk of address space, [base .. base+len-1], + has become undefined. This is used on amd64-linux and + some ppc variants to pass stack-redzoning hints to whoever + wants to see them. It also indicates the address of the + next (dynamic) instruction that will be executed. This is + to help Memcheck to origin tracking. + + ppIRStmt output: ====== AbiHint(<base>, <len>, <nia>) ====== + eg. ====== AbiHint(t1, 16, t2) ====== + */ + struct { + IRExpr* base; /* Start of undefined chunk */ + Int len; /* Length of undefined chunk */ + IRExpr* nia; /* Address of next (guest) insn */ + } AbiHint; + + /* Write a guest register, at a fixed offset in the guest state. + ppIRStmt output: PUT(<offset>) = <data>, eg. PUT(60) = t1 + */ + struct { + Int offset; /* Offset into the guest state */ + IRExpr* data; /* The value to write */ + } Put; + + /* Write a guest register, at a non-fixed offset in the guest + state. See the comment for GetI expressions for more + information. + + ppIRStmt output: PUTI<descr>[<ix>,<bias>] = <data>, + eg. PUTI(64:8xF64)[t5,0] = t1 + */ + struct { + IRPutI* details; + } PutI; + + /* Assign a value to a temporary. Note that SSA rules require + each tmp is only assigned to once. IR sanity checking will + reject any block containing a temporary which is not assigned + to exactly once. + + ppIRStmt output: t<tmp> = <data>, eg. t1 = 3 + */ + struct { + IRTemp tmp; /* Temporary (LHS of assignment) */ + IRExpr* data; /* Expression (RHS of assignment) */ + } WrTmp; + + /* Write a value to memory. This is a normal store, not a + Store-Conditional. To represent a Store-Conditional, + instead use IRStmt.LLSC. + ppIRStmt output: ST<end>(<addr>) = <data>, eg. STle(t1) = t2 + */ + struct { + IREndness end; /* Endianness of the store */ + IRExpr* addr; /* store address */ + IRExpr* data; /* value to write */ + } Store; + + /* Guarded store. Note that this is defined to evaluate all + expression fields (addr, data) even if the guard evaluates + to false. + ppIRStmt output: + if (<guard>) ST<end>(<addr>) = <data> */ + struct { + IRStoreG* details; + } StoreG; + + /* Guarded load. Note that this is defined to evaluate all + expression fields (addr, alt) even if the guard evaluates + to false. + ppIRStmt output: + t<tmp> = if (<guard>) <cvt>(LD<end>(<addr>)) else <alt> */ + struct { + IRLoadG* details; + } LoadG; + + /* Do an atomic compare-and-swap operation. Semantics are + described above on a comment at the definition of IRCAS. + + ppIRStmt output: + t<tmp> = CAS<end>(<addr> :: <expected> -> <new>) + eg + t1 = CASle(t2 :: t3->Add32(t3,1)) + which denotes a 32-bit atomic increment + of a value at address t2 + + A double-element CAS may also be denoted, in which case <tmp>, + <expected> and <new> are all pairs of items, separated by + commas. + */ + struct { + IRCAS* details; + } CAS; + + /* Either Load-Linked or Store-Conditional, depending on + STOREDATA. + + If STOREDATA is NULL then this is a Load-Linked, meaning + that data is loaded from memory as normal, but a + 'reservation' for the address is also lodged in the + hardware. + + result = Load-Linked(addr, end) + + The data transfer type is the type of RESULT (I32, I64, + etc). ppIRStmt output: + + result = LD<end>-Linked(<addr>), eg. LDbe-Linked(t1) + + If STOREDATA is not NULL then this is a Store-Conditional, + hence: + + result = Store-Conditional(addr, storedata, end) + + The data transfer type is the type of STOREDATA and RESULT + has type Ity_I1. The store may fail or succeed depending + on the state of a previously lodged reservation on this + address. RESULT is written 1 if the store succeeds and 0 + if it fails. eg ppIRStmt output: + + result = ( ST<end>-Cond(<addr>) = <storedata> ) + eg t3 = ( STbe-Cond(t1, t2) ) + + In all cases, the address must be naturally aligned for + the transfer type -- any misaligned addresses should be + caught by a dominating IR check and side exit. This + alignment restriction exists because on at least some + LL/SC platforms (ppc), stwcx. etc will trap w/ SIGBUS on + misaligned addresses, and we have to actually generate + stwcx. on the host, and we don't want it trapping on the + host. + + Summary of rules for transfer type: + STOREDATA == NULL (LL): + transfer type = type of RESULT + STOREDATA != NULL (SC): + transfer type = type of STOREDATA, and RESULT :: Ity_I1 + */ + struct { + IREndness end; + IRTemp result; + IRExpr* addr; + IRExpr* storedata; /* NULL => LL, non-NULL => SC */ + } LLSC; + + /* Call (possibly conditionally) a C function that has side + effects (ie. is "dirty"). See the comments above the + IRDirty type declaration for more information. + + ppIRStmt output: + t<tmp> = DIRTY <guard> <effects> + ::: <callee>(<args>) + eg. + t1 = DIRTY t27 RdFX-gst(16,4) RdFX-gst(60,4) + ::: foo{0x380035f4}(t2) + */ + struct { + IRDirty* details; + } Dirty; + + /* A memory bus event - a fence, or acquisition/release of the + hardware bus lock. IR optimisation treats all these as fences + across which no memory references may be moved. + ppIRStmt output: MBusEvent-Fence, + MBusEvent-BusLock, MBusEvent-BusUnlock. + */ + struct { + IRMBusEvent event; + } MBE; + + /* Conditional exit from the middle of an IRSB. + ppIRStmt output: if (<guard>) goto {<jk>} <dst> + eg. if (t0:69) goto {Boring} 0x4000AAA:I32 + If <guard> is true, the guest state is also updated by + PUT-ing <dst> at <offsIP>. This is done because a + taken exit must update the guest program counter. + TODO-JIT: The condition is going to disappear, making it + unconditional exit. + */ + struct { + IRExpr* guard; /* Conditional expression */ + IRConst* dst; /* Jump target (constant only) */ + IRJumpKind jk; /* Jump kind */ + Int offsIP; /* Guest state offset for IP */ + } Exit; + + /* If-Then-Else control flow diamond. See IRIfThenElse for details. + + ppIRIfThenElse output: + if (<cond>) [<hint>] then { + <IRTempDefSet> + <IRStmtVec> + } else { + <IRTempDefSet> + <IRStmtVec> + } + <phi-nodes> + + eg. if (t3) [IfThenElse_ThenLikely] then { + t4:I32 t7:I32 + + t4=0x2 + t7=Add32(t2,t1) + } else { + t5:I32 + + t5=0x3 + } + t6=phi(t4,t5) */ + struct { + IRIfThenElse* details; + } IfThenElse; + } Ist; +}; /* Statement constructors. */ extern IRStmt* IRStmt_NoOp ( void ); @@ -2985,45 +3194,63 @@ extern IRStmt* IRStmt_Dirty ( IRDirty* details ); extern IRStmt* IRStmt_MBE ( IRMBusEvent event ); extern IRStmt* IRStmt_Exit ( IRExpr* guard, IRJumpKind jk, IRConst* dst, Int offsIP ); +extern IRStmt* IRStmt_IfThenElse(IRExpr* cond, IRIfThenElse_Hint hint, + IRStmtVec* then_leg, IRStmtVec* else_leg, + IRPhiVec* phi_nodes); -/* Deep-copy an IRStmt. */ -extern IRStmt* deepCopyIRStmt ( const IRStmt* ); +/* Deep-copy an IRStmt. + Parent is required for "if-then-else" statements. */ +extern IRStmt* deepCopyIRStmt(const IRStmt* src, IRStmtVec* parent); -/* Pretty-print an IRStmt. */ -extern void ppIRStmt ( const IRStmt* ); +/* Pretty-prints an IRStmt. 'tyenv' is eventually used for pretty-printing + nested IRStmtVec's and can be NULL. */ +extern void ppIRStmt(const IRStmt* stmt, const IRTypeEnv* tyenv, UInt depth); /* ------------------ Basic Blocks ------------------ */ -/* Type environments: a bunch of statements, expressions, etc, are - incomplete without an environment indicating the type of each - IRTemp. So this provides one. IR temporaries are really just - unsigned ints and so this provides an array, 0 .. n_types_used-1 of - them. +/* Type environments: a bunch of statements, expressions, etc, are incomplete + without an environment indicating the type of each IRTemp and its scope. + So this provides one. IR temporaries are really just unsigned ints so they + can used to index these two arrays: + - 'types' which gives IRTemp's type + - 'ids' which gives ID of the defining IRStmtVec */ -typedef - struct { - IRType* types; - Int types_size; - Int types_used; - } - IRTypeEnv; +struct _IRTypeEnv { + IRType* types; + IRStmtVecID* ids; + UInt size; + UInt used; +}; -/* Obtain a new IRTemp */ -extern IRTemp newIRTemp ( IRTypeEnv*, IRType ); +/* Obtain a new IRTemp. New IRTemp is allocated from 'tyenv' and is marked + as defined in 'stmts'->defset. */ +extern IRTemp newIRTemp(IRTypeEnv* tyenv, IRStmtVec* stmts, IRType); /* Deep-copy a type environment */ extern IRTypeEnv* deepCopyIRTypeEnv ( const IRTypeEnv* ); -/* Pretty-print a type environment */ +/* Pretty-print a type environment. Use ppIRTypeEnvDefd() if possible which + combines also information from an IRTempDefSet to print only IRTemp's which + are defined in a given IRStmtVec. */ extern void ppIRTypeEnv ( const IRTypeEnv* ); +/* Much like ppIRTypeEnv() but prints only IRTemp's which are defined in a given + IRStmtVec. */ +extern void ppIRTypeEnvDefd(const IRTypeEnv* tyenv, const IRTempDefSet* defset, + UInt depth); + +/* Ensures that this IRTypeEnv can hold at least new_size types and ids. + Useful for certain bulk transformations. */ +extern void ensureSpaceInIRTypeEnv(IRTypeEnv*, UInt new_size); + /* Code blocks, which in proper compiler terminology are superblocks (single entry, multiple exit code sequences) contain: - - A table giving a type for each temp (the "type environment") - - An expandable array of statements + - A type environment (giving type for each temp and where it is defined) + - A vector of statements + - A sequence used to get a unique IRStmtVecID for nested IRStmtVec's - An expression of type 32 or 64 bits, depending on the guest's word size, indicating the next destination if the block executes all the way to the end, without a side exit @@ -3036,17 +3263,16 @@ extern void ppIRTypeEnv ( const IRTypeEnv* ); */ typedef struct { - IRTypeEnv* tyenv; - IRStmt** stmts; - Int stmts_size; - Int stmts_used; - IRExpr* next; - IRJumpKind jumpkind; - Int offsIP; + IRTypeEnv* tyenv; + IRStmtVec* stmts; + IRStmtVecID id_seq; + IRExpr* next; + IRJumpKind jumpkind; + Int offsIP; } IRSB; -/* Allocate a new, uninitialised IRSB */ +/* Allocates an empty IRSB. The corresponding IRStmtVec has ID #0. */ extern IRSB* emptyIRSB ( void ); /* Deep-copy an IRSB */ @@ -3059,17 +3285,24 @@ extern IRSB* deepCopyIRSBExceptStmts ( const IRSB* ); /* Pretty-print an IRSB */ extern void ppIRSB ( const IRSB* ); -/* Append an IRStmt to an IRSB */ -extern void addStmtToIRSB ( IRSB*, IRStmt* ); +/* Append an IRStmt to the main IRStmtVec (ID #0) of an IRSB. + Function addStmtToIRStmtVec() should be used instead. */ +extern void addStmtToIRSB(IRSB*, IRStmt*) __attribute__ ((deprecated)); +extern void addStmtToIRStmtVec(IRStmtVec*, IRStmt*); + +extern IRStmtVecID nextIRStmtVecID(IRSB*); + +/* Allocates an empty IfThenElse, assigns it a valid IRStmtVecID + and sets the parent for both then and else legs. + The returned IRStmt is added to the parent IRStmtVec and ready to be used. */ +extern IRStmt *addEmptyIfThenElse(IRSB* bb, IRStmtVec* parent, IRExpr* cond, + IRIfThenElse_Hint hint); /*---------------------------------------------------------------*/ /*--- Helper functions for the IR ---*/ /*---------------------------------------------------------------*/ -/* For messing with IR type environments */ -extern IRTypeEnv* emptyIRTypeEnv ( void ); - /* What is the type of this expression? */ extern IRType typeOfIRConst ( const IRConst* ); extern IRType typeOfIRTemp ( const IRTypeEnv*, IRTemp ); @@ -3080,12 +3313,12 @@ extern void typeOfIRLoadGOp ( IRLoadGOp cvt, /*OUT*/IRType* t_res, /*OUT*/IRType* t_arg ); -/* Sanity check a BB of IR */ -extern void sanityCheckIRSB ( const IRSB* bb, - const HChar* caller, - Bool require_flatness, - IRType guest_word_size ); -extern Bool isFlatIRStmt ( const IRStmt* ); +/* Sanity check a BB of IR. */ +extern void sanityCheckIRSB(const IRSB* bb, + const HChar* caller, + Bool require_flatness, + IRType guest_word_size); +extern Bool isFlatIRSB(const IRSB*); /* Is this any value actually in the enumeration 'IRType' ? */ extern Bool isPlausibleIRType ( IRType ty ); @@ -3095,7 +3328,7 @@ extern Bool isPlausibleIRType ( IRType ty ); /*--- IR injection ---*/ /*---------------------------------------------------------------*/ -void vex_inject_ir(IRSB *, IREndness); +void vex_inject_ir(IRSB* , IREndness); #endif /* ndef __LIBVEX_IR_H */ |
|
From: Ivo R. <ir...@so...> - 2017-08-28 22:26:18
|
https://sourceware.org/git/gitweb.cgi?p=valgrind.git;h=45b9d41ea0fd40e102557116e063d9eea8de3c9f commit 45b9d41ea0fd40e102557116e063d9eea8de3c9f Author: Ivo Raisr <iv...@iv...> Date: Mon Aug 28 23:14:55 2017 +0200 Focus initially on x86 architecture and Memcheck tool. Diff: --- Makefile.am | 26 +++++++++++++++----------- Makefile.vex.am | 28 ++-------------------------- 2 files changed, 17 insertions(+), 37 deletions(-) diff --git a/Makefile.am b/Makefile.am index fdce3cf..85f7dc4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,17 +4,21 @@ AUTOMAKE_OPTIONS = foreign 1.10 dist-bzip2 include $(top_srcdir)/Makefile.all.am TOOLS = memcheck \ - cachegrind \ - callgrind \ - massif \ - lackey \ - none \ - helgrind \ - drd - -EXP_TOOLS = exp-sgcheck \ - exp-bbv \ - exp-dhat + none + +# TODO-JIT: +# cachegrind \ +# callgrind \ +# massif \ +# lackey \ +# helgrind \ +# drd + +EXP_TOOLS = +# TODO-JIT: +# exp-sgcheck \ +# exp-bbv \ +# exp-dhat # Put docs last because building the HTML is slow and we want to get # everything else working before we try it. diff --git a/Makefile.vex.am b/Makefile.vex.am index 4ad5ffa..e0c17e4 100644 --- a/Makefile.vex.am +++ b/Makefile.vex.am @@ -125,18 +125,6 @@ LIBVEX_SOURCES_COMMON = \ priv/guest_generic_x87.c \ priv/guest_x86_helpers.c \ priv/guest_x86_toIR.c \ - priv/guest_amd64_helpers.c \ - priv/guest_amd64_toIR.c \ - priv/guest_ppc_helpers.c \ - priv/guest_ppc_toIR.c \ - priv/guest_arm_helpers.c \ - priv/guest_arm_toIR.c \ - priv/guest_arm64_helpers.c \ - priv/guest_arm64_toIR.c \ - priv/guest_s390_helpers.c \ - priv/guest_s390_toIR.c \ - priv/guest_mips_helpers.c \ - priv/guest_mips_toIR.c \ priv/host_generic_regs.c \ priv/host_generic_simd64.c \ priv/host_generic_simd128.c \ @@ -145,20 +133,8 @@ LIBVEX_SOURCES_COMMON = \ priv/host_generic_reg_alloc2.c \ priv/host_generic_reg_alloc3.c \ priv/host_x86_defs.c \ - priv/host_x86_isel.c \ - priv/host_amd64_defs.c \ - priv/host_amd64_isel.c \ - priv/host_ppc_defs.c \ - priv/host_ppc_isel.c \ - priv/host_arm_defs.c \ - priv/host_arm_isel.c \ - priv/host_arm64_defs.c \ - priv/host_arm64_isel.c \ - priv/host_s390_defs.c \ - priv/host_s390_isel.c \ - priv/s390_disasm.c \ - priv/host_mips_defs.c \ - priv/host_mips_isel.c + priv/host_x86_isel.c +# TODO-JIT: other architectures disabled for now LIBVEXMULTIARCH_SOURCES = priv/multiarch_main_main.c |
|
From: Ivo R. <ir...@so...> - 2017-08-28 22:26:11
|
The branch 'jit-hacks-2' was created pointing to: 613905c... Add some support for If-Then-Else into VEX register allocat |
|
From: Ivo R. <ir...@so...> - 2017-08-28 10:40:38
|
https://sourceware.org/git/gitweb.cgi?p=valgrind.git;h=efa1e5ef8d257e3b20facf6f04350d29578ae9e4 commit efa1e5ef8d257e3b20facf6f04350d29578ae9e4 Author: Ivo Raisr <iv...@iv...> Date: Sat Aug 26 00:19:05 2017 +0200 VEX register allocator version 3. Implements a new version of VEX register allocator which keeps the main state per virtual registers, as opposed to real registers in v2. This results in a simpler and cleaner design and much simpler implementation. It has been observed that the new allocator executes 20-30% faster than the previous one but could produce slightly worse spilling decisions. Overall performance improvement when running the Valgrind performance regression test suite has been observed in terms of a few percent. The new register allocator (v3) is now the default one. The old register allocator (v2) is still kept around and can be activated with command line option '--vex-regalloc-version=2'. Fixes BZ#381553. Diff: --- Makefile.vex.am | 1 + NEWS | 1 + VEX/priv/host_amd64_defs.c | 43 +- VEX/priv/host_amd64_defs.h | 29 +- VEX/priv/host_arm64_defs.c | 52 +- VEX/priv/host_arm64_defs.h | 3 +- VEX/priv/host_arm_defs.c | 44 +- VEX/priv/host_arm_defs.h | 3 +- VEX/priv/host_generic_reg_alloc2.c | 179 ++---- VEX/priv/host_generic_reg_alloc3.c | 1171 ++++++++++++++++++++++++++++++++++++ VEX/priv/host_generic_regs.c | 49 +- VEX/priv/host_generic_regs.h | 108 ++-- VEX/priv/host_mips_defs.c | 39 +- VEX/priv/host_mips_defs.h | 3 +- VEX/priv/host_ppc_defs.c | 37 +- VEX/priv/host_ppc_defs.h | 3 +- VEX/priv/host_s390_defs.c | 25 +- VEX/priv/host_s390_defs.h | 3 +- VEX/priv/host_x86_defs.c | 36 +- VEX/priv/host_x86_defs.h | 4 +- VEX/priv/main_main.c | 36 +- VEX/priv/main_util.c | 37 +- VEX/pub/libvex.h | 5 + coregrind/m_main.c | 3 + none/tests/cmdline2.stdout.exp | 1 + 25 files changed, 1628 insertions(+), 287 deletions(-) diff --git a/Makefile.vex.am b/Makefile.vex.am index 9b9b9b5..4ad5ffa 100644 --- a/Makefile.vex.am +++ b/Makefile.vex.am @@ -143,6 +143,7 @@ LIBVEX_SOURCES_COMMON = \ priv/host_generic_simd256.c \ priv/host_generic_maddf.c \ priv/host_generic_reg_alloc2.c \ + priv/host_generic_reg_alloc3.c \ priv/host_x86_defs.c \ priv/host_x86_isel.c \ priv/host_amd64_defs.c \ diff --git a/NEWS b/NEWS index 516c4cc..446a7fa 100644 --- a/NEWS +++ b/NEWS @@ -40,6 +40,7 @@ where XXXXXX is the bug number as listed below. 381272 ppc64 doesn't compile test_isa_2_06_partx.c without VSX support 381289 epoll_pwait can have a NULL sigmask 381274 powerpc too chatty even with --sigill-diagnostics=no +381553 VEX register allocator v3 381769 Use ucontext_t instead of struct ucontext 381805 arm32 needs ld.so index hardwire for new glibc security fixes 382256 gz compiler flag test doesn't work for gold diff --git a/VEX/priv/host_amd64_defs.c b/VEX/priv/host_amd64_defs.c index 5e0600a..ebe2b00 100644 --- a/VEX/priv/host_amd64_defs.c +++ b/VEX/priv/host_amd64_defs.c @@ -63,6 +63,7 @@ const RRegUniverse* getRRegUniverse_AMD64 ( void ) /* Add the registers. The initial segment of this array must be those available for allocation by reg-alloc, and those that follow are not available for allocation. */ + ru->allocable_start[HRcInt64] = ru->size; ru->regs[ru->size++] = hregAMD64_RSI(); ru->regs[ru->size++] = hregAMD64_RDI(); ru->regs[ru->size++] = hregAMD64_R8(); @@ -72,6 +73,10 @@ const RRegUniverse* getRRegUniverse_AMD64 ( void ) ru->regs[ru->size++] = hregAMD64_R14(); ru->regs[ru->size++] = hregAMD64_R15(); ru->regs[ru->size++] = hregAMD64_RBX(); + ru->regs[ru->size++] = hregAMD64_R10(); + ru->allocable_end[HRcInt64] = ru->size - 1; + + ru->allocable_start[HRcVec128] = ru->size; ru->regs[ru->size++] = hregAMD64_XMM3(); ru->regs[ru->size++] = hregAMD64_XMM4(); ru->regs[ru->size++] = hregAMD64_XMM5(); @@ -82,8 +87,9 @@ const RRegUniverse* getRRegUniverse_AMD64 ( void ) ru->regs[ru->size++] = hregAMD64_XMM10(); ru->regs[ru->size++] = hregAMD64_XMM11(); ru->regs[ru->size++] = hregAMD64_XMM12(); - ru->regs[ru->size++] = hregAMD64_R10(); + ru->allocable_end[HRcVec128] = ru->size - 1; ru->allocable = ru->size; + /* And other regs, not available to the allocator. */ ru->regs[ru->size++] = hregAMD64_RAX(); ru->regs[ru->size++] = hregAMD64_RCX(); @@ -101,7 +107,7 @@ const RRegUniverse* getRRegUniverse_AMD64 ( void ) } -void ppHRegAMD64 ( HReg reg ) +UInt ppHRegAMD64 ( HReg reg ) { Int r; static const HChar* ireg64_names[16] @@ -109,27 +115,24 @@ void ppHRegAMD64 ( HReg reg ) "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15" }; /* Be generic for all virtual regs. */ if (hregIsVirtual(reg)) { - ppHReg(reg); - return; + return ppHReg(reg); } /* But specific for real regs. */ switch (hregClass(reg)) { case HRcInt64: r = hregEncoding(reg); vassert(r >= 0 && r < 16); - vex_printf("%s", ireg64_names[r]); - return; + return vex_printf("%s", ireg64_names[r]); case HRcVec128: r = hregEncoding(reg); vassert(r >= 0 && r < 16); - vex_printf("%%xmm%d", r); - return; + return vex_printf("%%xmm%d", r); default: vpanic("ppHRegAMD64"); } } -static void ppHRegAMD64_lo32 ( HReg reg ) +static UInt ppHRegAMD64_lo32 ( HReg reg ) { Int r; static const HChar* ireg32_names[16] @@ -137,17 +140,16 @@ static void ppHRegAMD64_lo32 ( HReg reg ) "%r8d", "%r9d", "%r10d", "%r11d", "%r12d", "%r13d", "%r14d", "%r15d" }; /* Be generic for all virtual regs. */ if (hregIsVirtual(reg)) { - ppHReg(reg); - vex_printf("d"); - return; + UInt written = ppHReg(reg); + written += vex_printf("d"); + return written; } /* But specific for real regs. */ switch (hregClass(reg)) { case HRcInt64: r = hregEncoding(reg); vassert(r >= 0 && r < 16); - vex_printf("%s", ireg32_names[r]); - return; + return vex_printf("%s", ireg32_names[r]); default: vpanic("ppHRegAMD64_lo32: invalid regclass"); } @@ -1995,6 +1997,19 @@ void genReload_AMD64 ( /*OUT*/HInstr** i1, /*OUT*/HInstr** i2, } } +AMD64Instr* genMove_AMD64(HReg from, HReg to, Bool mode64) +{ + switch (hregClass(from)) { + case HRcInt64: + return AMD64Instr_Alu64R(Aalu_MOV, AMD64RMI_Reg(from), to); + case HRcVec128: + return AMD64Instr_SseReRg(Asse_MOV, from, to); + default: + ppHRegClass(hregClass(from)); + vpanic("genMove_AMD64: unimplemented regclass"); + } +} + AMD64Instr* directReload_AMD64( AMD64Instr* i, HReg vreg, Short spill_off ) { vassert(spill_off >= 0 && spill_off < 10000); /* let's say */ diff --git a/VEX/priv/host_amd64_defs.h b/VEX/priv/host_amd64_defs.h index 39682ef..8a3eea8 100644 --- a/VEX/priv/host_amd64_defs.h +++ b/VEX/priv/host_amd64_defs.h @@ -56,19 +56,18 @@ ST_IN HReg hregAMD64_R13 ( void ) { return mkHReg(False, HRcInt64, 13, 5); } ST_IN HReg hregAMD64_R14 ( void ) { return mkHReg(False, HRcInt64, 14, 6); } ST_IN HReg hregAMD64_R15 ( void ) { return mkHReg(False, HRcInt64, 15, 7); } ST_IN HReg hregAMD64_RBX ( void ) { return mkHReg(False, HRcInt64, 3, 8); } - -ST_IN HReg hregAMD64_XMM3 ( void ) { return mkHReg(False, HRcVec128, 3, 9); } -ST_IN HReg hregAMD64_XMM4 ( void ) { return mkHReg(False, HRcVec128, 4, 10); } -ST_IN HReg hregAMD64_XMM5 ( void ) { return mkHReg(False, HRcVec128, 5, 11); } -ST_IN HReg hregAMD64_XMM6 ( void ) { return mkHReg(False, HRcVec128, 6, 12); } -ST_IN HReg hregAMD64_XMM7 ( void ) { return mkHReg(False, HRcVec128, 7, 13); } -ST_IN HReg hregAMD64_XMM8 ( void ) { return mkHReg(False, HRcVec128, 8, 14); } -ST_IN HReg hregAMD64_XMM9 ( void ) { return mkHReg(False, HRcVec128, 9, 15); } -ST_IN HReg hregAMD64_XMM10 ( void ) { return mkHReg(False, HRcVec128, 10, 16); } -ST_IN HReg hregAMD64_XMM11 ( void ) { return mkHReg(False, HRcVec128, 11, 17); } -ST_IN HReg hregAMD64_XMM12 ( void ) { return mkHReg(False, HRcVec128, 12, 18); } - -ST_IN HReg hregAMD64_R10 ( void ) { return mkHReg(False, HRcInt64, 10, 19); } +ST_IN HReg hregAMD64_R10 ( void ) { return mkHReg(False, HRcInt64, 10, 9); } + +ST_IN HReg hregAMD64_XMM3 ( void ) { return mkHReg(False, HRcVec128, 3, 10); } +ST_IN HReg hregAMD64_XMM4 ( void ) { return mkHReg(False, HRcVec128, 4, 11); } +ST_IN HReg hregAMD64_XMM5 ( void ) { return mkHReg(False, HRcVec128, 5, 12); } +ST_IN HReg hregAMD64_XMM6 ( void ) { return mkHReg(False, HRcVec128, 6, 13); } +ST_IN HReg hregAMD64_XMM7 ( void ) { return mkHReg(False, HRcVec128, 7, 14); } +ST_IN HReg hregAMD64_XMM8 ( void ) { return mkHReg(False, HRcVec128, 8, 15); } +ST_IN HReg hregAMD64_XMM9 ( void ) { return mkHReg(False, HRcVec128, 9, 16); } +ST_IN HReg hregAMD64_XMM10 ( void ) { return mkHReg(False, HRcVec128, 10, 17); } +ST_IN HReg hregAMD64_XMM11 ( void ) { return mkHReg(False, HRcVec128, 11, 18); } +ST_IN HReg hregAMD64_XMM12 ( void ) { return mkHReg(False, HRcVec128, 12, 19); } ST_IN HReg hregAMD64_RAX ( void ) { return mkHReg(False, HRcInt64, 0, 20); } ST_IN HReg hregAMD64_RCX ( void ) { return mkHReg(False, HRcInt64, 1, 21); } @@ -81,7 +80,7 @@ ST_IN HReg hregAMD64_XMM0 ( void ) { return mkHReg(False, HRcVec128, 0, 26); } ST_IN HReg hregAMD64_XMM1 ( void ) { return mkHReg(False, HRcVec128, 1, 27); } #undef ST_IN -extern void ppHRegAMD64 ( HReg ); +extern UInt ppHRegAMD64 ( HReg ); /* --------- Condition codes, AMD encoding. --------- */ @@ -801,7 +800,7 @@ extern void genSpill_AMD64 ( /*OUT*/HInstr** i1, /*OUT*/HInstr** i2, HReg rreg, Int offset, Bool ); extern void genReload_AMD64 ( /*OUT*/HInstr** i1, /*OUT*/HInstr** i2, HReg rreg, Int offset, Bool ); - +extern AMD64Instr* genMove_AMD64(HReg from, HReg to, Bool); extern AMD64Instr* directReload_AMD64 ( AMD64Instr* i, HReg vreg, Short spill_off ); diff --git a/VEX/priv/host_arm64_defs.c b/VEX/priv/host_arm64_defs.c index 380a24d..2506512 100644 --- a/VEX/priv/host_arm64_defs.c +++ b/VEX/priv/host_arm64_defs.c @@ -64,7 +64,7 @@ const RRegUniverse* getRRegUniverse_ARM64 ( void ) /* Add the registers. The initial segment of this array must be those available for allocation by reg-alloc, and those that follow are not available for allocation. */ - + ru->allocable_start[HRcInt64] = ru->size; ru->regs[ru->size++] = hregARM64_X22(); ru->regs[ru->size++] = hregARM64_X23(); ru->regs[ru->size++] = hregARM64_X24(); @@ -81,6 +81,7 @@ const RRegUniverse* getRRegUniverse_ARM64 ( void ) ru->regs[ru->size++] = hregARM64_X5(); ru->regs[ru->size++] = hregARM64_X6(); ru->regs[ru->size++] = hregARM64_X7(); + ru->allocable_end[HRcInt64] = ru->size - 1; // X8 is used as a ProfInc temporary, not available to regalloc. // X9 is a chaining/spill temporary, not available to regalloc. @@ -94,19 +95,23 @@ const RRegUniverse* getRRegUniverse_ARM64 ( void ) // X21 is the guest state pointer, not available to regalloc. // vector regs. Unfortunately not callee-saved. + ru->allocable_start[HRcVec128] = ru->size; ru->regs[ru->size++] = hregARM64_Q16(); ru->regs[ru->size++] = hregARM64_Q17(); ru->regs[ru->size++] = hregARM64_Q18(); ru->regs[ru->size++] = hregARM64_Q19(); ru->regs[ru->size++] = hregARM64_Q20(); + ru->allocable_end[HRcVec128] = ru->size - 1; // F64 regs, all of which are callee-saved + ru->allocable_start[HRcFlt64] = ru->size; ru->regs[ru->size++] = hregARM64_D8(); ru->regs[ru->size++] = hregARM64_D9(); ru->regs[ru->size++] = hregARM64_D10(); ru->regs[ru->size++] = hregARM64_D11(); ru->regs[ru->size++] = hregARM64_D12(); ru->regs[ru->size++] = hregARM64_D13(); + ru->allocable_end[HRcFlt64] = ru->size - 1; ru->allocable = ru->size; /* And other regs, not available to the allocator. */ @@ -142,43 +147,41 @@ const RRegUniverse* getRRegUniverse_ARM64 ( void ) } -void ppHRegARM64 ( HReg reg ) { +UInt ppHRegARM64 ( HReg reg ) { Int r; /* Be generic for all virtual regs. */ if (hregIsVirtual(reg)) { - ppHReg(reg); - return; + return ppHReg(reg); } /* But specific for real regs. */ switch (hregClass(reg)) { case HRcInt64: r = hregEncoding(reg); vassert(r >= 0 && r < 31); - vex_printf("x%d", r); - return; + return vex_printf("x%d", r); case HRcFlt64: r = hregEncoding(reg); vassert(r >= 0 && r < 32); - vex_printf("d%d", r); - return; + return vex_printf("d%d", r); case HRcVec128: r = hregEncoding(reg); vassert(r >= 0 && r < 32); - vex_printf("q%d", r); - return; + return vex_printf("q%d", r); default: vpanic("ppHRegARM64"); } } -static void ppHRegARM64asSreg ( HReg reg ) { - ppHRegARM64(reg); - vex_printf("(S-reg)"); +static UInt ppHRegARM64asSreg ( HReg reg ) { + UInt written = ppHRegARM64(reg); + written += vex_printf("(S-reg)"); + return written; } -static void ppHRegARM64asHreg ( HReg reg ) { - ppHRegARM64(reg); - vex_printf("(H-reg)"); +static UInt ppHRegARM64asHreg ( HReg reg ) { + UInt written = ppHRegARM64(reg); + written += vex_printf("(H-reg)"); + return written; } @@ -1745,7 +1748,7 @@ void ppARM64Instr ( const ARM64Instr* i ) { ppHRegARM64asSreg(i->ARM64in.VCmpS.argR); return; case ARM64in_VFCSel: { - void (*ppHRegARM64fp)(HReg) + UInt (*ppHRegARM64fp)(HReg) = (i->ARM64in.VFCSel.isD ? ppHRegARM64 : ppHRegARM64asSreg); vex_printf("fcsel "); ppHRegARM64fp(i->ARM64in.VFCSel.dst); @@ -2616,6 +2619,21 @@ void genReload_ARM64 ( /*OUT*/HInstr** i1, /*OUT*/HInstr** i2, } } +ARM64Instr* genMove_ARM64(HReg from, HReg to, Bool mode64) +{ + switch (hregClass(from)) { + case HRcInt64: + return ARM64Instr_MovI(to, from); + case HRcFlt64: + return ARM64Instr_VMov(8, to, from); + case HRcVec128: + return ARM64Instr_VMov(16, to, from); + default: + ppHRegClass(hregClass(from)); + vpanic("genMove_ARM64: unimplemented regclass"); + } +} + /* Emit an instruction into buf and return the number of bytes used. Note that buf is not the insn's final place, and therefore it is diff --git a/VEX/priv/host_arm64_defs.h b/VEX/priv/host_arm64_defs.h index 14b2de6..e7da4f9 100644 --- a/VEX/priv/host_arm64_defs.h +++ b/VEX/priv/host_arm64_defs.h @@ -74,7 +74,7 @@ ST_IN HReg hregARM64_X9 ( void ) { return mkHReg(False, HRcInt64, 9, 27); } ST_IN HReg hregARM64_X21 ( void ) { return mkHReg(False, HRcInt64, 21, 28); } #undef ST_IN -extern void ppHRegARM64 ( HReg ); +extern UInt ppHRegARM64 ( HReg ); /* Number of registers used arg passing in function calls */ #define ARM64_N_ARGREGS 8 /* x0 .. x7 */ @@ -1007,6 +1007,7 @@ extern void genSpill_ARM64 ( /*OUT*/HInstr** i1, /*OUT*/HInstr** i2, HReg rreg, Int offset, Bool ); extern void genReload_ARM64 ( /*OUT*/HInstr** i1, /*OUT*/HInstr** i2, HReg rreg, Int offset, Bool ); +extern ARM64Instr* genMove_ARM64(HReg from, HReg to, Bool); extern const RRegUniverse* getRRegUniverse_ARM64 ( void ); diff --git a/VEX/priv/host_arm_defs.c b/VEX/priv/host_arm_defs.c index a986f37..9bf87cd 100644 --- a/VEX/priv/host_arm_defs.c +++ b/VEX/priv/host_arm_defs.c @@ -68,6 +68,7 @@ const RRegUniverse* getRRegUniverse_ARM ( void ) /* Callee saves ones are listed first, since we prefer them if they're available. */ + ru->allocable_start[HRcInt32] = ru->size; ru->regs[ru->size++] = hregARM_R4(); ru->regs[ru->size++] = hregARM_R5(); ru->regs[ru->size++] = hregARM_R6(); @@ -80,24 +81,34 @@ const RRegUniverse* getRRegUniverse_ARM ( void ) ru->regs[ru->size++] = hregARM_R2(); ru->regs[ru->size++] = hregARM_R3(); ru->regs[ru->size++] = hregARM_R9(); + ru->allocable_end[HRcInt32] = ru->size - 1; + /* FP registers. Note: these are all callee-save. Yay! Hence we don't need to mention them as trashed in getHRegUsage for ARMInstr_Call. */ + ru->allocable_start[HRcFlt64] = ru->size; ru->regs[ru->size++] = hregARM_D8(); ru->regs[ru->size++] = hregARM_D9(); ru->regs[ru->size++] = hregARM_D10(); ru->regs[ru->size++] = hregARM_D11(); ru->regs[ru->size++] = hregARM_D12(); + ru->allocable_end[HRcFlt64] = ru->size - 1; + + ru->allocable_start[HRcFlt32] = ru->size; ru->regs[ru->size++] = hregARM_S26(); ru->regs[ru->size++] = hregARM_S27(); ru->regs[ru->size++] = hregARM_S28(); ru->regs[ru->size++] = hregARM_S29(); ru->regs[ru->size++] = hregARM_S30(); + ru->allocable_end[HRcFlt32] = ru->size - 1; + + ru->allocable_start[HRcVec128] = ru->size; ru->regs[ru->size++] = hregARM_Q8(); ru->regs[ru->size++] = hregARM_Q9(); ru->regs[ru->size++] = hregARM_Q10(); ru->regs[ru->size++] = hregARM_Q11(); ru->regs[ru->size++] = hregARM_Q12(); + ru->allocable_end[HRcVec128] = ru->size - 1; ru->allocable = ru->size; /* And other regs, not available to the allocator. */ @@ -140,35 +151,30 @@ const RRegUniverse* getRRegUniverse_ARM ( void ) } -void ppHRegARM ( HReg reg ) { +UInt ppHRegARM ( HReg reg ) { Int r; /* Be generic for all virtual regs. */ if (hregIsVirtual(reg)) { - ppHReg(reg); - return; + return ppHReg(reg); } /* But specific for real regs. */ switch (hregClass(reg)) { case HRcInt32: r = hregEncoding(reg); vassert(r >= 0 && r < 16); - vex_printf("r%d", r); - return; + return vex_printf("r%d", r); case HRcFlt64: r = hregEncoding(reg); vassert(r >= 0 && r < 32); - vex_printf("d%d", r); - return; + return vex_printf("d%d", r); case HRcFlt32: r = hregEncoding(reg); vassert(r >= 0 && r < 32); - vex_printf("s%d", r); - return; + return vex_printf("s%d", r); case HRcVec128: r = hregEncoding(reg); vassert(r >= 0 && r < 16); - vex_printf("q%d", r); - return; + return vex_printf("q%d", r); default: vpanic("ppHRegARM"); } @@ -2772,6 +2778,22 @@ void genReload_ARM ( /*OUT*/HInstr** i1, /*OUT*/HInstr** i2, } } +ARMInstr* genMove_ARM(HReg from, HReg to, Bool mode64) +{ + switch (hregClass(from)) { + case HRcInt32: + return ARMInstr_Mov(to, ARMRI84_R(from)); + case HRcFlt32: + return ARMInstr_VUnaryS(ARMvfpu_COPY, to, from); + case HRcFlt64: + return ARMInstr_VUnaryD(ARMvfpu_COPY, to, from); + case HRcVec128: + return ARMInstr_NUnary(ARMneon_COPY, to, from, 4, False); + default: + ppHRegClass(hregClass(from)); + vpanic("genMove_ARM: unimplemented regclass"); + } +} /* Emit an instruction into buf and return the number of bytes used. Note that buf is not the insn's final place, and therefore it is diff --git a/VEX/priv/host_arm_defs.h b/VEX/priv/host_arm_defs.h index e8a2eb7..56c4ec5 100644 --- a/VEX/priv/host_arm_defs.h +++ b/VEX/priv/host_arm_defs.h @@ -81,7 +81,7 @@ ST_IN HReg hregARM_Q14 ( void ) { return mkHReg(False, HRcVec128, 14, 32); } ST_IN HReg hregARM_Q15 ( void ) { return mkHReg(False, HRcVec128, 15, 33); } #undef ST_IN -extern void ppHRegARM ( HReg ); +extern UInt ppHRegARM ( HReg ); /* Number of registers used arg passing in function calls */ #define ARM_N_ARGREGS 4 /* r0, r1, r2, r3 */ @@ -1070,6 +1070,7 @@ extern void genSpill_ARM ( /*OUT*/HInstr** i1, /*OUT*/HInstr** i2, HReg rreg, Int offset, Bool ); extern void genReload_ARM ( /*OUT*/HInstr** i1, /*OUT*/HInstr** i2, HReg rreg, Int offset, Bool ); +extern ARMInstr* genMove_ARM(HReg from, HReg to, Bool); extern const RRegUniverse* getRRegUniverse_ARM ( void ); diff --git a/VEX/priv/host_generic_reg_alloc2.c b/VEX/priv/host_generic_reg_alloc2.c index 2294a9b..ec291d3 100644 --- a/VEX/priv/host_generic_reg_alloc2.c +++ b/VEX/priv/host_generic_reg_alloc2.c @@ -294,49 +294,6 @@ static inline UInt ULong__minIndex ( ULong w64 ) { } -/* Vectorised memset, copied from Valgrind's m_libcbase.c. */ -static void* local_memset ( void *destV, Int c, SizeT sz ) -{ -# define IS_4_ALIGNED(aaa_p) (0 == (((HWord)(aaa_p)) & ((HWord)0x3))) - - UInt c4; - UChar* d = destV; - UChar uc = c; - - while ((!IS_4_ALIGNED(d)) && sz >= 1) { - d[0] = uc; - d++; - sz--; - } - if (sz == 0) - return destV; - c4 = uc; - c4 |= (c4 << 8); - c4 |= (c4 << 16); - while (sz >= 16) { - ((UInt*)d)[0] = c4; - ((UInt*)d)[1] = c4; - ((UInt*)d)[2] = c4; - ((UInt*)d)[3] = c4; - d += 16; - sz -= 16; - } - while (sz >= 4) { - ((UInt*)d)[0] = c4; - d += 4; - sz -= 4; - } - while (sz >= 1) { - d[0] = c; - d++; - sz--; - } - return destV; - -# undef IS_4_ALIGNED -} - - /* A target-independent register allocator. Requires various functions which it uses to deal abstractly with instructions and registers, since it cannot have any target-specific knowledge. @@ -352,44 +309,13 @@ static void* local_memset ( void *destV, Int c, SizeT sz ) Takes an expandable array of pointers to unallocated insns. Returns an expandable array of pointers to allocated insns. */ -HInstrArray* doRegisterAllocation ( +HInstrArray* doRegisterAllocation_v2 ( /* Incoming virtual-registerised code. */ HInstrArray* instrs_in, - /* The real-register universe to use. This contains facts about - real registers, one of which is the set of registers available - for allocation. */ - const RRegUniverse* univ, - - /* Return True iff the given insn is a reg-reg move, in which - case also return the src and dst regs. */ - Bool (*isMove) ( const HInstr*, HReg*, HReg* ), - - /* Get info about register usage in this insn. */ - void (*getRegUsage) ( HRegUsage*, const HInstr*, Bool ), - - /* Apply a reg-reg mapping to an insn. */ - void (*mapRegs) ( HRegRemap*, HInstr*, Bool ), - - /* Return one, or, if we're unlucky, two insn(s) to spill/restore a - real reg to a spill slot byte offset. The two leading HInstr** - args are out parameters, through which the generated insns are - returned. Also (optionally) a 'directReload' function, which - attempts to replace a given instruction by one which reads - directly from a specified spill slot. May be NULL, in which - case the optimisation is not attempted. */ - void (*genSpill) ( HInstr**, HInstr**, HReg, Int, Bool ), - void (*genReload) ( HInstr**, HInstr**, HReg, Int, Bool ), - HInstr* (*directReload) ( HInstr*, HReg, Short ), - Int guest_sizeB, - - /* For debug printing only. */ - void (*ppInstr) ( const HInstr*, Bool ), - void (*ppReg) ( HReg ), - - /* 32/64bit mode */ - Bool mode64 + /* Register allocator controls to use. */ + const RegAllocControl* con ) { # define N_SPILL64S (LibVEX_N_SPILL_BYTES / 8) @@ -447,7 +373,7 @@ HInstrArray* doRegisterAllocation ( not at each insn processed. */ Bool do_sanity_check; - vassert(0 == (guest_sizeB % LibVEX_GUEST_STATE_ALIGN)); + vassert(0 == (con->guest_sizeB % LibVEX_GUEST_STATE_ALIGN)); vassert(0 == (LibVEX_N_SPILL_BYTES % LibVEX_GUEST_STATE_ALIGN)); vassert(0 == (N_SPILL64S % 2)); @@ -463,7 +389,7 @@ HInstrArray* doRegisterAllocation ( HInstr* _tmp = (_instr); \ if (DEBUG_REGALLOC) { \ vex_printf("** "); \ - (*ppInstr)(_tmp, mode64); \ + con->ppInstr(_tmp, con->mode64); \ vex_printf("\n\n"); \ } \ addHInstr ( instrs_out, _tmp ); \ @@ -474,13 +400,13 @@ HInstrArray* doRegisterAllocation ( Int z, q; \ for (z = 0; z < n_rregs; z++) { \ vex_printf(" rreg_state[%2d] = ", z); \ - (*ppReg)(univ->regs[z]); \ + con->ppReg(con->univ->regs[z]); \ vex_printf(" \t"); \ switch (rreg_state[z].disp) { \ case Free: vex_printf("Free\n"); break; \ case Unavail: vex_printf("Unavail\n"); break; \ case Bound: vex_printf("BoundTo "); \ - (*ppReg)(rreg_state[z].vreg); \ + con->ppReg(rreg_state[z].vreg); \ vex_printf("\n"); break; \ } \ } \ @@ -505,7 +431,7 @@ HInstrArray* doRegisterAllocation ( /* ... and initialise running state. */ /* n_rregs is no more than a short name for n_available_real_regs. */ - n_rregs = univ->allocable; + n_rregs = con->univ->allocable; n_vregs = instrs_in->n_vregs; /* If this is not so, vreg_state entries will overflow. */ @@ -586,13 +512,13 @@ HInstrArray* doRegisterAllocation ( for (Int ii = 0; ii < instrs_in->arr_used; ii++) { - (*getRegUsage)( ®_usage_arr[ii], instrs_in->arr[ii], mode64 ); + con->getRegUsage(®_usage_arr[ii], instrs_in->arr[ii], con->mode64); if (0) { vex_printf("\n%d stage1: ", ii); - (*ppInstr)(instrs_in->arr[ii], mode64); + con->ppInstr(instrs_in->arr[ii], con->mode64); vex_printf("\n"); - ppHRegUsage(univ, ®_usage_arr[ii]); + ppHRegUsage(con->univ, ®_usage_arr[ii]); } /* ------ start of DEAL WITH VREG LIVE RANGES ------ */ @@ -606,7 +532,7 @@ HInstrArray* doRegisterAllocation ( Int k = hregIndex(vreg); if (k < 0 || k >= n_vregs) { vex_printf("\n"); - (*ppInstr)(instrs_in->arr[ii], mode64); + con->ppInstr(instrs_in->arr[ii], con->mode64); vex_printf("\n"); vex_printf("vreg %d, n_vregs %d\n", k, n_vregs); vpanic("doRegisterAllocation: out-of-range vreg"); @@ -711,10 +637,10 @@ HInstrArray* doRegisterAllocation ( } else if (!isW && isR) { if (rreg_live_after[j] == INVALID_INSTRNO) { vex_printf("\nOFFENDING RREG = "); - (*ppReg)(univ->regs[j]); + con->ppReg(con->univ->regs[j]); vex_printf("\n"); vex_printf("\nOFFENDING instr = "); - (*ppInstr)(instrs_in->arr[ii], mode64); + con->ppInstr(instrs_in->arr[ii], con->mode64); vex_printf("\n"); vpanic("doRegisterAllocation: " "first event for rreg is Read"); @@ -724,10 +650,10 @@ HInstrArray* doRegisterAllocation ( vassert(isR && isW); if (rreg_live_after[j] == INVALID_INSTRNO) { vex_printf("\nOFFENDING RREG = "); - (*ppReg)(univ->regs[j]); + con->ppReg(con->univ->regs[j]); vex_printf("\n"); vex_printf("\nOFFENDING instr = "); - (*ppInstr)(instrs_in->arr[ii], mode64); + con->ppInstr(instrs_in->arr[ii], con->mode64); vex_printf("\n"); vpanic("doRegisterAllocation: " "first event for rreg is Modify"); @@ -741,7 +667,7 @@ HInstrArray* doRegisterAllocation ( ensureRRLRspace(&rreg_lrs_la, &rreg_lrs_size, rreg_lrs_used); if (0) vex_printf("FLUSH 1 (%d,%d)\n", flush_la, flush_db); - rreg_lrs_la[rreg_lrs_used].rreg = univ->regs[j]; + rreg_lrs_la[rreg_lrs_used].rreg = con->univ->regs[j]; rreg_lrs_la[rreg_lrs_used].live_after = toShort(flush_la); rreg_lrs_la[rreg_lrs_used].dead_before = toShort(flush_db); rreg_lrs_used++; @@ -778,7 +704,7 @@ HInstrArray* doRegisterAllocation ( if (0) vex_printf("FLUSH 2 (%d,%d)\n", rreg_live_after[j], rreg_dead_before[j]); - rreg_lrs_la[rreg_lrs_used].rreg = univ->regs[j]; + rreg_lrs_la[rreg_lrs_used].rreg = con->univ->regs[j]; rreg_lrs_la[rreg_lrs_used].live_after = toShort(rreg_live_after[j]); rreg_lrs_la[rreg_lrs_used].dead_before = toShort(rreg_dead_before[j]); rreg_lrs_used++; @@ -805,7 +731,7 @@ HInstrArray* doRegisterAllocation ( for (Int j = 0; j < n_rregs; j++) { if (!rreg_state[j].has_hlrs) continue; - ppReg(univ->regs[j]); + con->ppReg(con->univ->regs[j]); vex_printf(" hinted\n"); } } @@ -841,14 +767,14 @@ HInstrArray* doRegisterAllocation ( vex_printf("RRegLRs by LA:\n"); for (Int j = 0; j < rreg_lrs_used; j++) { vex_printf(" "); - (*ppReg)(rreg_lrs_la[j].rreg); + con->ppReg(rreg_lrs_la[j].rreg); vex_printf(" la = %d, db = %d\n", rreg_lrs_la[j].live_after, rreg_lrs_la[j].dead_before ); } vex_printf("RRegLRs by DB:\n"); for (Int j = 0; j < rreg_lrs_used; j++) { vex_printf(" "); - (*ppReg)(rreg_lrs_db[j].rreg); + con->ppReg(rreg_lrs_db[j].rreg); vex_printf(" la = %d, db = %d\n", rreg_lrs_db[j].live_after, rreg_lrs_db[j].dead_before ); } @@ -882,7 +808,7 @@ HInstrArray* doRegisterAllocation ( */ /* Int max_ss_no = -1; */ - local_memset(ss_busy_until_before, 0, sizeof(ss_busy_until_before)); + vex_bzero(ss_busy_until_before, sizeof(ss_busy_until_before)); for (Int j = 0; j < n_vregs; j++) { @@ -940,7 +866,7 @@ HInstrArray* doRegisterAllocation ( /* This reflects LibVEX's hard-wired knowledge of the baseBlock layout: the guest state, then two equal sized areas following it for two sets of shadow state, and then the spill area. */ - vreg_lrs[j].spill_offset = toShort(guest_sizeB * 3 + ss_no * 8); + vreg_lrs[j].spill_offset = toShort(con->guest_sizeB * 3 + ss_no * 8); /* Independent check that we've made a sane choice of slot */ sanity_check_spill_offset( &vreg_lrs[j] ); @@ -983,7 +909,7 @@ HInstrArray* doRegisterAllocation ( if (DEBUG_REGALLOC) { vex_printf("\n====----====---- Insn %d ----====----====\n", ii); vex_printf("---- "); - (*ppInstr)(instrs_in->arr[ii], mode64); + con->ppInstr(instrs_in->arr[ii], con->mode64); vex_printf("\n\nInitial state:\n"); PRINT_STATE; vex_printf("\n"); @@ -1018,7 +944,7 @@ HInstrArray* doRegisterAllocation ( vex_printf("considering la %d .. db %d reg = ", rreg_lrs_la[j].live_after, rreg_lrs_la[j].dead_before); - (*ppReg)(reg); + con->ppReg(reg); vex_printf("\n"); } @@ -1059,7 +985,7 @@ HInstrArray* doRegisterAllocation ( vassert(rreg_state[j].eq_spill_slot == False); continue; } - vassert(hregClass(univ->regs[j]) + vassert(hregClass(con->univ->regs[j]) == hregClass(rreg_state[j].vreg)); vassert( hregIsVirtual(rreg_state[j].vreg)); } @@ -1099,7 +1025,7 @@ HInstrArray* doRegisterAllocation ( the dst to the src's rreg, and that's all. */ HReg vregS = INVALID_HREG; HReg vregD = INVALID_HREG; - if ( (*isMove)( instrs_in->arr[ii], &vregS, &vregD ) ) { + if ( con->isMove(instrs_in->arr[ii], &vregS, &vregD) ) { if (!hregIsVirtual(vregS)) goto cannot_coalesce; if (!hregIsVirtual(vregD)) goto cannot_coalesce; /* Check that *isMove is not telling us a bunch of lies ... */ @@ -1112,9 +1038,9 @@ HInstrArray* doRegisterAllocation ( if (vreg_lrs[m].live_after != ii) goto cannot_coalesce; if (DEBUG_REGALLOC) { vex_printf("COALESCE "); - (*ppReg)(vregS); + con->ppReg(vregS); vex_printf(" -> "); - (*ppReg)(vregD); + con->ppReg(vregD); vex_printf("\n\n"); } /* Find the state entry for vregS. */ @@ -1163,7 +1089,7 @@ HInstrArray* doRegisterAllocation ( vreg_state[m] = INVALID_RREG_NO; if (DEBUG_REGALLOC) { vex_printf("free up "); - (*ppReg)(univ->regs[j]); + con->ppReg(con->univ->regs[j]); vex_printf("\n"); } } @@ -1204,7 +1130,7 @@ HInstrArray* doRegisterAllocation ( than before it. */ if (DEBUG_REGALLOC) { vex_printf("need to free up rreg: "); - (*ppReg)(rreg_lrs_la[rreg_lrs_la_next].rreg); + con->ppReg(rreg_lrs_la[rreg_lrs_la_next].rreg); vex_printf("\n\n"); } Int k = hregIndex(rreg_lrs_la[rreg_lrs_la_next].rreg); @@ -1223,8 +1149,8 @@ HInstrArray* doRegisterAllocation ( if ((!eq_spill_opt) || !rreg_state[k].eq_spill_slot) { HInstr* spill1 = NULL; HInstr* spill2 = NULL; - (*genSpill)( &spill1, &spill2, univ->regs[k], - vreg_lrs[m].spill_offset, mode64 ); + con->genSpill(&spill1, &spill2, con->univ->regs[k], + vreg_lrs[m].spill_offset, con->mode64); vassert(spill1 || spill2); /* can't both be NULL */ if (spill1) EMIT_INSTR(spill1); @@ -1271,7 +1197,7 @@ HInstrArray* doRegisterAllocation ( that the change is invisible to the standard-case handling that follows. */ - if (directReload && reg_usage_arr[ii].n_vRegs <= 2) { + if (con->directReload != NULL && reg_usage_arr[ii].n_vRegs <= 2) { Bool debug_direct_reload = False; HReg cand = INVALID_HREG; Bool nreads = 0; @@ -1305,19 +1231,20 @@ HInstrArray* doRegisterAllocation ( vassert(! sameHReg(reg_usage_arr[ii].vRegs[0], reg_usage_arr[ii].vRegs[1])); - reloaded = directReload ( instrs_in->arr[ii], cand, spilloff ); + reloaded = con->directReload(instrs_in->arr[ii], cand, spilloff); if (debug_direct_reload && !reloaded) { vex_printf("[%3d] ", spilloff); ppHReg(cand); vex_printf(" "); - ppInstr(instrs_in->arr[ii], mode64); + con->ppInstr(instrs_in->arr[ii], con->mode64); } if (reloaded) { /* Update info about the insn, so it looks as if it had been in this form all along. */ instrs_in->arr[ii] = reloaded; - (*getRegUsage)( ®_usage_arr[ii], instrs_in->arr[ii], mode64 ); + con->getRegUsage(®_usage_arr[ii], instrs_in->arr[ii], + con->mode64); if (debug_direct_reload && !reloaded) { vex_printf(" --> "); - ppInstr(reloaded, mode64); + con->ppInstr(reloaded, con->mode64); } } @@ -1336,7 +1263,7 @@ HInstrArray* doRegisterAllocation ( vassert(hregIsVirtual(vreg)); if (0) { - vex_printf("considering "); (*ppReg)(vreg); vex_printf("\n"); + vex_printf("considering "); con->ppReg(vreg); vex_printf("\n"); } /* Now we're trying to find a rreg for "vreg". First of all, @@ -1347,7 +1274,7 @@ HInstrArray* doRegisterAllocation ( Int n = vreg_state[m]; if (IS_VALID_RREGNO(n)) { vassert(rreg_state[n].disp == Bound); - addToHRegRemap(&remap, vreg, univ->regs[n]); + addToHRegRemap(&remap, vreg, con->univ->regs[n]); /* If this rreg is written or modified, mark it as different from any spill slot value. */ if (reg_usage_arr[ii].vMode[j] != HRmRead) @@ -1366,7 +1293,7 @@ HInstrArray* doRegisterAllocation ( Int k; for (k = 0; k < n_rregs; k++) { if (rreg_state[k].disp != Free - || hregClass(univ->regs[k]) != hregClass(vreg)) + || hregClass(con->univ->regs[k]) != hregClass(vreg)) continue; if (rreg_state[k].has_hlrs) { /* Well, at least we can use k_suboptimal if we really @@ -1387,7 +1314,7 @@ HInstrArray* doRegisterAllocation ( Int p = hregIndex(vreg); vassert(IS_VALID_VREGNO(p)); vreg_state[p] = toShort(k); - addToHRegRemap(&remap, vreg, univ->regs[k]); + addToHRegRemap(&remap, vreg, con->univ->regs[k]); /* Generate a reload if needed. This only creates needed reloads because the live range builder for vregs will guarantee that the first event for a vreg is a write. @@ -1398,8 +1325,8 @@ HInstrArray* doRegisterAllocation ( vassert(vreg_lrs[p].reg_class != HRcINVALID); HInstr* reload1 = NULL; HInstr* reload2 = NULL; - (*genReload)( &reload1, &reload2, univ->regs[k], - vreg_lrs[p].spill_offset, mode64 ); + con->genReload(&reload1, &reload2, con->univ->regs[k], + vreg_lrs[p].spill_offset, con->mode64); vassert(reload1 || reload2); /* can't both be NULL */ if (reload1) EMIT_INSTR(reload1); @@ -1433,7 +1360,7 @@ HInstrArray* doRegisterAllocation ( rreg_state[k].is_spill_cand = False; if (rreg_state[k].disp != Bound) continue; - if (hregClass(univ->regs[k]) != hregClass(vreg)) + if (hregClass(con->univ->regs[k]) != hregClass(vreg)) continue; rreg_state[k].is_spill_cand = True; /* Note, the following loop visits only the virtual regs @@ -1468,7 +1395,7 @@ HInstrArray* doRegisterAllocation ( vassert(IS_VALID_RREGNO(spillee)); vassert(rreg_state[spillee].disp == Bound); /* check it's the right class */ - vassert(hregClass(univ->regs[spillee]) == hregClass(vreg)); + vassert(hregClass(con->univ->regs[spillee]) == hregClass(vreg)); /* check we're not ejecting the vreg for which we are trying to free up a register. */ vassert(! sameHReg(rreg_state[spillee].vreg, vreg)); @@ -1483,8 +1410,8 @@ HInstrArray* doRegisterAllocation ( if ((!eq_spill_opt) || !rreg_state[spillee].eq_spill_slot) { HInstr* spill1 = NULL; HInstr* spill2 = NULL; - (*genSpill)( &spill1, &spill2, univ->regs[spillee], - vreg_lrs[m].spill_offset, mode64 ); + con->genSpill(&spill1, &spill2, con->univ->regs[spillee], + vreg_lrs[m].spill_offset, con->mode64); vassert(spill1 || spill2); /* can't both be NULL */ if (spill1) EMIT_INSTR(spill1); @@ -1509,8 +1436,8 @@ HInstrArray* doRegisterAllocation ( vassert(vreg_lrs[m].reg_class != HRcINVALID); HInstr* reload1 = NULL; HInstr* reload2 = NULL; - (*genReload)( &reload1, &reload2, univ->regs[spillee], - vreg_lrs[m].spill_offset, mode64 ); + con->genReload(&reload1, &reload2, con->univ->regs[spillee], + vreg_lrs[m].spill_offset, con->mode64); vassert(reload1 || reload2); /* can't both be NULL */ if (reload1) EMIT_INSTR(reload1); @@ -1529,7 +1456,7 @@ HInstrArray* doRegisterAllocation ( /* So after much twisting and turning, we have vreg mapped to rreg_state[spillee].rreg. Note that in the map. */ - addToHRegRemap(&remap, vreg, univ->regs[spillee]); + addToHRegRemap(&remap, vreg, con->univ->regs[spillee]); } /* iterate over virtual registers in this instruction. */ @@ -1545,7 +1472,7 @@ HInstrArray* doRegisterAllocation ( */ /* NOTE, DESTRUCTIVELY MODIFIES instrs_in->arr[ii]. */ - (*mapRegs)( &remap, instrs_in->arr[ii], mode64 ); + con->mapRegs(&remap, instrs_in->arr[ii], con->mode64); EMIT_INSTR( instrs_in->arr[ii] ); if (DEBUG_REGALLOC) { diff --git a/VEX/priv/host_generic_reg_alloc3.c b/VEX/priv/host_generic_reg_alloc3.c new file mode 100644 index 0000000..f798372 --- /dev/null +++ b/VEX/priv/host_generic_reg_alloc3.c @@ -0,0 +1,1171 @@ +/*----------------------------------------------------------------------------*/ +/*--- begin host_generic_reg_alloc3.c ---*/ +/*----------------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation framework. + + Copyright (C) 2017-2017 Ivo Raisr + iv...@iv... + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "libvex_basictypes.h" +#include "libvex.h" + +#include "main_util.h" +#include "host_generic_regs.h" + +/* Set to 1 for lots of debugging output. */ +#define DEBUG_REGALLOC 0 + +/* Set to 1 for sanity checking at every instruction. + Set to 0 for sanity checking only every 17th one and the last one. */ +#define SANITY_CHECKS_EVERY_INSTR 0 + + +#define INVALID_INSTRNO (-2) + +/* Register allocator state is kept in an array of VRegState's. + There is an element for every virtual register (vreg). + Elements are indexed [0 .. n_vregs-1]. + Records information about vreg live range and its state. */ +typedef + struct { + /* Live range, register class and spill offset are computed during the + first register allocator pass and remain unchanged after that. */ + + /* This vreg becomes live with this instruction (inclusive). Contains + either an instruction number or INVALID_INSTRNO. */ + Short live_after; + /* This vreg becomes dead before this instruction (exclusive). Contains + either an instruction number or INVALID_INSTRNO. */ + Short dead_before; + /* What kind of register this is. */ + HRegClass reg_class; + + /* What is its current disposition? */ + enum { Unallocated, /* Neither spilled nor assigned to a real reg. */ + Assigned, /* Assigned to a real register, viz rreg. */ + Spilled /* Spilled to the spill slot. */ + } disp; + + /* If .disp == Assigned, what rreg is it bound to? */ + HReg rreg; + + /* The "home" spill slot. The offset is relative to the beginning of + the guest state. */ + UShort spill_offset; + } + VRegState; + +/* The allocator also maintains a redundant array of indexes (rreg_state) from + rreg numbers back to entries in vreg_state. It is redundant because iff + rreg_state[r] == v then hregNumber(vreg_state[v].rreg) == r -- that is, the + two entries point at each other. The purpose of this is to speed up + activities which involve looking for a particular rreg: there is no need to + scan the vreg_state looking for it, just index directly into rreg_state. + The FAQ "does this rreg already have an associated vreg" is the main + beneficiary. + The identity of the real register is not recorded here, because the index + of this structure in |rreg_state| is the index number of the register, and + the register itself can be extracted from the RRegUniverse (univ). */ +typedef + struct { + /* What is its current disposition? */ + enum { Free, /* Not bound to any vreg. */ + Bound, /* Bound to a vreg, viz vreg. */ + Reserved /* Reserved for an instruction. */ + } disp; + + /* If .disp == Bound, what vreg is it bound to? */ + HReg vreg; + } + RRegState; + +/* Records information on a real-register live range, associated with + a particular real register. Computed once; does not change. */ +typedef + struct { + /* This rreg becomes live with this instruction (inclusive). Contains + either an instruction number or INVALID_INSTRNO. */ + Short live_after; + /* This rreg becomes dead before this instruction (exclusive). Contains + either an instruction number or INVALID_INSTRNO. */ + Short dead_before; + } + RRegLR; + +/* Live ranges for a single rreg and the current one. + Live ranges are computed during the first register allocator pass and remain + unchanged after that. + The identity of the real register is not recorded here, because the index + of this structure in |rreg_lr_state| is the index number of the register, and + the register itself can be extracted from the RRegUniverse (univ). */ +typedef + struct { + RRegLR* lrs; + UInt lrs_size; + UInt lrs_used; + + /* Live range corresponding to the currently processed instruction. + Points into |lrs| array. */ + RRegLR *lr_current; + UInt lr_current_idx; + } + RRegLRState; + +#define IS_VALID_VREGNO(v) ((v) >= 0 && (v) < n_vregs) +#define IS_VALID_RREGNO(r) ((r) >= 0 && (r) < n_rregs) + +/* Compute the index of the highest and lowest 1 in a ULong, respectively. + Results are undefined if the argument is zero. Don't pass it zero :) */ +static inline UInt ULong__maxIndex ( ULong w64 ) { + return 63 - __builtin_clzll(w64); +} + +static inline UInt ULong__minIndex ( ULong w64 ) { + return __builtin_ctzll(w64); +} + +static inline void enlarge_rreg_lrs(RRegLRState* rreg_lrs) +{ + vassert(rreg_lrs->lrs_used == rreg_lrs->lrs_size); + + RRegLR* lr2 = LibVEX_Alloc_inline(2 * rreg_lrs->lrs_used * sizeof(RRegLR)); + for (UInt l = 0; l < rreg_lrs->lrs_used; l++) { + lr2[l] = rreg_lrs->lrs[l]; + } + + rreg_lrs->lrs = lr2; + rreg_lrs->lrs_size = 2 * rreg_lrs->lrs_used; +} + +static inline void print_state( + const RegAllocControl* con, + const VRegState* vreg_state, UInt n_vregs, + const RRegState* rreg_state, UInt n_rregs, + const RRegLRState* rreg_lr_state, + UShort current_ii) +{ + for (UInt v_idx = 0; v_idx < n_vregs; v_idx++) { + const VRegState* vreg = &vreg_state[v_idx]; + + if (vreg->live_after == INVALID_INSTRNO) { + continue; /* This is a dead vreg. Never comes into live. */ + } + vex_printf("vreg_state[%3u] \t", v_idx); + + UInt written; + switch (vreg->disp) { + case Unallocated: + written = vex_printf("unallocated"); + break; + case Assigned: + written = vex_printf("assigned to "); + written += con->ppReg(vreg->rreg); + break; + case Spilled: + written = vex_printf("spilled at offset %u", vreg->spill_offset); + break; + default: + vassert(0); + } + + for (Int w = 30 - written; w > 0; w--) { + vex_printf(" "); + } + + if (vreg->live_after > (Short) current_ii) { + vex_printf("[not live yet]\n"); + } else if ((Short) current_ii >= vreg->dead_before) { + vex_printf("[now dead]\n"); + } else { + vex_printf("[live]\n"); + } + } + + for (UInt r_idx = 0; r_idx < n_rregs; r_idx++) { + const RRegState* rreg = &rreg_state[r_idx]; + const RRegLRState* rreg_lrs = &rreg_lr_state[r_idx]; + vex_printf("rreg_state[%2u] = ", r_idx); + UInt written = con->ppReg(con->univ->regs[r_idx]); + for (Int w = 10 - written; w > 0; w--) { + vex_printf(" "); + } + + switch (rreg->disp) { + case Free: + vex_printf("free\n"); + break; + case Bound: + vex_printf("bound for "); + con->ppReg(rreg->vreg); + vex_printf("\n"); + break; + case Reserved: + vex_printf("reserved - live range [%d, %d)\n", + rreg_lrs->lr_current->live_after, + rreg_lrs->lr_current->dead_before); + break; + } + } +} + +static inline void emit_instr(HInstr* instr, HInstrArray* instrs_out, + const RegAllocControl* con, const HChar* why) +{ + if (DEBUG_REGALLOC) { + vex_printf("** "); + con->ppInstr(instr, con->mode64); + if (why != NULL) { + vex_printf(" (%s)", why); + } + vex_printf("\n\n"); + } + + addHInstr(instrs_out, instr); +} + +/* Spills a vreg assigned to some rreg. + The vreg is spilled and the rreg is freed. + Returns rreg's index. */ +static inline UInt spill_vreg( + HReg vreg, UInt v_idx, UInt current_ii, VRegState* vreg_state, UInt n_vregs, + RRegState* rreg_state, UInt n_rregs, HInstrArray* instrs_out, + const RegAllocControl* con) +{ + /* Check some invariants first. */ + vassert(IS_VALID_VREGNO((v_idx))); + vassert(vreg_state[v_idx].disp == Assigned); + HReg rreg = vreg_state[v_idx].rreg; + UInt r_idx = hregIndex(rreg); + vassert(IS_VALID_RREGNO(r_idx)); + vassert(hregClass(con->univ->regs[r_idx]) == hregClass(vreg)); + vassert(vreg_state[v_idx].dead_before > (Short) current_ii); + vassert(vreg_state[v_idx].reg_class != HRcINVALID); + + /* Generate spill. */ + HInstr* spill1 = NULL; + HInstr* spill2 = NULL; + con->genSpill(&spill1, &spill2, rreg, vreg_state[v_idx].spill_offset, + con->mode64); + vassert(spill1 != NULL || spill2 != NULL); /* cannot be both NULL */ + if (spill1 != NULL) { + emit_instr(spill1, instrs_out, con, "spill1"); + } + if (spill2 != NULL) { + emit_instr(spill2, instrs_out, con, "spill2"); + } + + /* Update register allocator state. */ + vreg_state[v_idx].disp = Spilled; + vreg_state[v_idx].rreg = INVALID_HREG; + rreg_state[r_idx].disp = Free; + rreg_state[r_idx].vreg = INVALID_HREG; + + return r_idx; +} + +/* Chooses a vreg to be spilled based on various criteria. + The vreg must not be from the instruction being processed, that is, it must + not be listed in reg_usage->vRegs. */ +static inline HReg find_vreg_to_spill( + VRegState* vreg_state, UInt n_vregs, + RRegState* rreg_state, UInt n_rregs, + const HRegUsage* instr_regusage, HRegClass target_hregclass, + const HRegUsage* reg_usage, UInt scan_forward_from, UInt scan_forward_max, + const RegAllocControl* con) +{ + /* Scan forwards a few instructions to find the most distant mentioned + use of a vreg. We can scan in the range of (inclusive): + - reg_usage[scan_forward_from] + - reg_usage[scan_forward_end], where scan_forward_end + = MIN(scan_forward_max, scan_forward_from + FEW_INSTRUCTIONS). */ +# define FEW_INSTRUCTIONS 5 + UInt scan_forward_end + = (scan_forward_max <= scan_forward_from + FEW_INSTRUCTIONS) ? + scan_forward_max : scan_forward_from + FEW_INSTRUCTIONS; +# undef FEW_INSTRUCTIONS + + HReg vreg_found = INVALID_HREG; + UInt distance_so_far = 0; + + for (UInt r_idx = con->univ->allocable_start[target_hregclass]; + r_idx <= con->univ->allocable_end[target_hregclass]; r_idx++) { + if (rreg_state[r_idx].disp == Bound) { + HReg vreg = rreg_state[r_idx].vreg; + if (! HRegUsage__contains(instr_regusage, vreg)) { + UInt ii = scan_forward_from; + for ( ; ii <= scan_forward_end; ii++) { + if (HRegUsage__contains(®_usage[ii], vreg)) { + break; + } + } + + if (ii - scan_forward_from > distance_so_far) { + distance_so_far = ii = scan_forward_from; + vreg_found = vreg; + if (ii + distance_so_far == scan_forward_end) { + break; /* We are at the end. Nothing could be better. */ + } + } + } + } + } + + if (hregIsInvalid(vreg_found)) { + vex_printf("doRegisterAllocation_v3: cannot find a register in class: "); + ppHRegClass(target_hregclass); + vex_printf("\n"); + vpanic("doRegisterAllocation_v3: cannot find a register."); + } + + return vreg_found; +} + +/* Find a free rreg of the correct class. + Tries to find an rreg whose live range (if any) is as far ahead in the + incoming instruction stream as possible. An ideal rreg candidate is + a callee-save register because it won't be used for parameter passing + around helper function calls. */ +static Bool find_free_rreg( + VRegState* vreg_state, UInt n_vregs, + RRegState* rreg_state, UInt n_rregs, + const RRegLRState* rreg_lr_state, + UInt current_ii, HRegClass target_hregclass, + Bool reserve_phase, const RegAllocControl* con, UInt* r_idx_found) +{ + Bool found = False; + UInt distance_so_far = 0; /* running max for |live_after - current_ii| */ + + for (UInt r_idx = con->univ->allocable_start[target_hregclass]; + r_idx <= con->univ->allocable_end[target_hregclass]; r_idx++) { + const RRegState* rreg = &rreg_state[r_idx]; + const RRegLRState* rreg_lrs = &rreg_lr_state[r_idx]; + if (rreg->disp == Free) { + if (rreg_lrs->lrs_used == 0) { + found = True; + *r_idx_found = r_idx; + break; /* There could be nothing better, so break now. */ + } else { + const RRegLR* lr = rreg_lrs->lr_current; + if (lr->live_after > (Short) current_ii) { + /* Not live, yet. */ + if ((lr->live_after - (Short) current_ii) > distance_so_far) { + distance_so_far = lr->live_after - (Short) current_ii; + found = True; + *r_idx_found = r_idx; + } + } else if ((Short) current_ii >= lr->dead_before) { + /* Now dead. Effectively as if there is no LR now. */ + found = True; + *r_idx_found = r_idx; + break; /* There could be nothing better, so break now. */ + } else { + /* Going live for this instruction. This could happen only when + rregs are being reserved en mass, for example before + a helper call. */ + vassert(reserve_phase); + } + } + } + } + + return found; +} + +/* A target-independent register allocator (v3). Requires various functions + which it uses to deal abstractly with instructions and registers, since it + cannot have any target-specific knowledge. + + Returns a new list of instructions, which, as a result of the behaviour of + mapRegs, will be in-place modifications of the original instructions. + + Requires that the incoming code has been generated using vreg numbers + 0, 1 .. n_vregs-1. Appearance of a vreg outside that range is a checked + run-time error. + + Takes unallocated instructions and returns allocated instructions. +*/ +HInstrArray* doRegisterAllocation_v3( + /* Incoming virtual-registerised code. */ + HInstrArray* instrs_in, + + /* Register allocator controls to use. */ + const RegAllocControl* con +) +{ + vassert((con->guest_sizeB % LibVEX_GUEST_STATE_ALIGN) == 0); + + /* The main register allocator state. */ + UInt n_vregs = instrs_in->n_vregs; + VRegState* vreg_state = NULL; + if (n_vregs > 0) { + vreg_state = LibVEX_Alloc_inline(n_vregs * sizeof(VRegState)); + } + + /* If this is not so, the universe we have is nonsensical. */ + UInt n_rregs = con->univ->allocable; + vassert(n_rregs > 0); + STATIC_ASSERT(N_RREGUNIVERSE_REGS == 64); + + /* Redundant rreg -> vreg state. */ + RRegState* rreg_state = LibVEX_Alloc_inline(n_rregs * sizeof(RRegState)); + + /* Info on rreg live ranges. */ + RRegLRState* rreg_lr_state + = LibVEX_Alloc_inline(n_rregs * sizeof(RRegLRState)); + + /* Info on register usage in the incoming instruction array. Computed once + and remains unchanged, more or less; updated sometimes by the + direct-reload optimisation. */ + HRegUsage* reg_usage + = LibVEX_Alloc_inline(sizeof(HRegUsage) * instrs_in->arr_used); + + /* The live range numbers are signed shorts, and so limiting the + number of instructions to 15000 comfortably guards against them + overflowing 32k. */ + vassert(instrs_in->arr_used <= 15000); + + /* The output array of instructions. */ + HInstrArray* instrs_out = newHInstrArray(); + + +# define OFFENDING_VREG(_v_idx, _instr, _mode) \ + do { \ + vex_printf("\n\nOffending vreg = %u\n", (_v_idx)); \ + vex_printf("\nOffending instruction = "); \ + con->ppInstr((_instr), con->mode64); \ + vex_printf("\n"); \ + vpanic("doRegisterAllocation_v3: first event for vreg is "#_mode \ + " (should be Write)"); \ + } while (0) + +# define OFFENDING_RREG(_r_idx, _instr, _mode) \ + do { \ + vex_printf("\n\nOffending rreg = "); \ + con->ppReg(con->univ->regs[(_r_idx)]); \ + vex_printf("\nOffending instruction = "); \ + con->ppInstr((_instr), con->mode64); \ + vex_printf("\n"); \ + vpanic("doRegisterAllocation_v3: first event for rreg is "#_mode \ + " (should be Write)"); \ + } while (0) + + +/* Finds an rreg of the correct class. + If a free rreg is not found, then spills a vreg not used by the current + instruction and makes free the corresponding rreg. */ +# define FIND_OR_MAKE_FREE_RREG(_ii, _v_idx, _reg_class, _reserve_phase) \ + ({ \ + UInt _r_free_idx = -1; \ + Bool free_rreg_found = find_free_rreg( \ + vreg_state, n_vregs, rreg_state, n_rregs, rreg_lr_state, \ + (_ii), (_reg_class), (_reserve_phase), con, &_r_free_idx); \ + if (!free_rreg_found) { \ + HReg vreg_to_spill = find_vreg_to_spill( \ + vreg_state, n_vregs, rreg_state, n_rregs, \ + ®_usage[(_ii)], (_reg_class), \ + reg_usage, (_ii) + 1, \ + instrs_in->arr_used - 1, con); \ + _r_free_idx = spill_vreg(vreg_to_spill, hregIndex(vreg_to_spill), \ + (_ii), v... [truncated message content] |
|
From: Philippe W. <phi...@so...> - 2017-08-26 07:02:37
|
https://sourceware.org/git/gitweb.cgi?p=valgrind.git;h=a34a4ebd13ccc2954553477cf90facc8ee971627 commit a34a4ebd13ccc2954553477cf90facc8ee971627 Author: Philippe Waroquiers <phi...@sk...> Date: Sat Aug 26 09:01:42 2017 +0200 Follow up to 9f9ad501 : ensure we only have a single X if several files modified Diff: --- auxprogs/make_or_upd_vgversion_h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auxprogs/make_or_upd_vgversion_h b/auxprogs/make_or_upd_vgversion_h index 3bccb28..7787b4c 100755 --- a/auxprogs/make_or_upd_vgversion_h +++ b/auxprogs/make_or_upd_vgversion_h @@ -7,7 +7,7 @@ extract_git_version() REV=$(git show --format=%H#%ci -s $(git rev-parse HEAD) | sed -e 's/ .*//' -e 's/[0-9a-f]\{30\}#/#/' -e 's/-//g' \ -e 's/#/-/') - X=$(git status -s -uno | sed -e 's/.*/X/') + X=$(git status -s -uno | sed -e 's/.*/X/' | uniq) echo $REV$X else echo "unknown" |
|
From: Philippe W. <phi...@so...> - 2017-08-26 06:50:56
|
https://sourceware.org/git/gitweb.cgi?p=valgrind.git;h=9f9ad50182016708b8eb430a7f7896687240e5ce commit 9f9ad50182016708b8eb430a7f7896687240e5ce Author: Philippe Waroquiers <phi...@sk...> Date: Sat Aug 26 08:47:46 2017 +0200 Add to -v --version commit date+(optional) trailing X char if some files are changed Diff: --- auxprogs/make_or_upd_vgversion_h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/auxprogs/make_or_upd_vgversion_h b/auxprogs/make_or_upd_vgversion_h index 92b0708..3bccb28 100755 --- a/auxprogs/make_or_upd_vgversion_h +++ b/auxprogs/make_or_upd_vgversion_h @@ -4,7 +4,11 @@ extract_git_version() { if [ -d "$1"/.git ] then - git rev-parse --short=10 HEAD + REV=$(git show --format=%H#%ci -s $(git rev-parse HEAD) | + sed -e 's/ .*//' -e 's/[0-9a-f]\{30\}#/#/' -e 's/-//g' \ + -e 's/#/-/') + X=$(git status -s -uno | sed -e 's/.*/X/') + echo $REV$X else echo "unknown" fi @@ -20,6 +24,11 @@ else /* Do not edit: file generated by auxprogs/make_or_upd_vgversion_h. This file defines VGGIT, used to report GIT revision when using command line options: -v --version + The produced VGGIT format is + hhhhhhhhhh-YYYYMMDDX + where hhhhhhhhhh is the first 10 characters of the HEAD commit + YYYYMMDD is the commit date + Trailing X is present if there are any (non untracked) modified files. */ #define VGGIT "$(extract_git_version .)" EOF |
|
From: John R. <jr...@bi...> - 2017-08-24 13:02:09
|
> My motivation is a huge binary which takes a lot of time to instrument Please show measurements to substantiate this claim. What is the total /usr/bin/size in bytes of all executable code in the main program and shared libraries? Instrumenting is performed Just-In-Time, with a cache. Is the cache too small for the working set of instructions, and therefore is thrashed much of the time, forcing the re-instrumentation of too many basic blocks? Or does even the first instrumentation for each basic block (that is executed) take too long? And how long is "too long"? |
|
From: Ivo R. <iv...@iv...> - 2017-08-24 08:39:18
|
Dear Valgrind developers, I had just a thought if it would be possible for Valgrind/Memchek to instrument a binary just once, save the instrumented code in a pre-image and then execute it many times already instrumented. My motivation is a huge binary which takes a lot of time to instrument and which is executed frequently during many many test suite runs without any human intervention. What would be the major challenges here? My preliminary idea was that trans-cache could request blocks either from VEX or from the pre-image. Please let me know your thoughts! Thank you, I. |
|
From: Petar J. <pe...@so...> - 2017-08-22 14:06:00
|
https://sourceware.org/git/gitweb.cgi?p=valgrind.git;h=a1e5547a44db46bab055b8907f129e5679b1d36a commit a1e5547a44db46bab055b8907f129e5679b1d36a Author: Petar Jovanovic <mip...@gm...> Date: Tue Aug 22 16:05:00 2017 +0200 mips: remove incorrect implementation of several Iops Remove incorrect implementation of Iop_DivModU64to32 Iop_DivModS64to32 Iop_DivModU128to64 Iop_DivModS128to64 Diff: --- VEX/priv/host_mips_isel.c | 66 ---------------------------------------- memcheck/tests/vbit-test/irops.c | 8 ++--- 2 files changed, 4 insertions(+), 70 deletions(-) diff --git a/VEX/priv/host_mips_isel.c b/VEX/priv/host_mips_isel.c index 2c49a6e..deb33f2 100644 --- a/VEX/priv/host_mips_isel.c +++ b/VEX/priv/host_mips_isel.c @@ -1188,36 +1188,6 @@ static HReg iselWordExpr_R_wrk(ISelEnv * env, IRExpr * e) return r_ccIR; } - if (e->Iex.Binop.op == Iop_DivModU64to32 || - e->Iex.Binop.op == Iop_DivModS64to32) { - HReg tLo = newVRegI(env); - HReg tHi = newVRegI(env); - HReg mask = newVRegI(env); - HReg tLo_1 = newVRegI(env); - HReg tHi_1 = newVRegI(env); - HReg r_dst = newVRegI(env); - Bool syned = toBool(e->Iex.Binop.op == Iop_DivModS64to32); - - HReg r_srcR = iselWordExpr_R(env, e->Iex.Binop.arg2); - HReg r_srcL = iselWordExpr_R(env, e->Iex.Binop.arg1); - - addInstr(env, MIPSInstr_Div(syned, True, r_srcL, r_srcR)); - addInstr(env, MIPSInstr_Mfhi(tHi)); - addInstr(env, MIPSInstr_Mflo(tLo)); - - addInstr(env, MIPSInstr_Shft(Mshft_SLL, False, tHi_1, tHi, - MIPSRH_Imm(False, 32))); - - addInstr(env, MIPSInstr_LI(mask, 0xffffffff)); - addInstr(env, MIPSInstr_Alu(Malu_AND, tLo_1, tLo, - MIPSRH_Reg(mask))); - - addInstr(env, MIPSInstr_Alu(Malu_OR, r_dst, tHi_1, - MIPSRH_Reg(tLo_1))); - - return r_dst; - } - if (e->Iex.Binop.op == Iop_DivModU32to32 || e->Iex.Binop.op == Iop_DivModS32to32) { HReg tLo = newVRegI(env); @@ -2262,25 +2232,6 @@ static void iselInt128Expr_wrk(HReg * rHi, HReg * rLo, ISelEnv * env, return; } - case Iop_DivModU128to64: - case Iop_DivModS128to64: { - vassert(mode64); - HReg rHi1, rLo1; - iselInt128Expr(&rHi1, &rLo1, env, e->Iex.Binop.arg1); - - HReg r_srcR = iselWordExpr_R(env, e->Iex.Binop.arg2); - HReg tLo = newVRegI(env); - HReg tHi = newVRegI(env); - Bool syned = toBool(e->Iex.Binop.op == Iop_DivModS128to64); - - addInstr(env, MIPSInstr_Div(syned, False, rLo1, r_srcR)); - addInstr(env, MIPSInstr_Mfhi(tHi)); - addInstr(env, MIPSInstr_Mflo(tLo)); - *rHi = tHi; - *rLo = tLo; - return; - } - default: break; } @@ -2475,23 +2426,6 @@ static void iselInt64Expr_wrk(HReg * rHi, HReg * rLo, ISelEnv * env, IRExpr * e) return; } - case Iop_DivModS64to32: - case Iop_DivModU64to32: { - HReg r_sHi, r_sLo; - HReg tLo = newVRegI(env); - HReg tHi = newVRegI(env); - Bool syned = toBool(op_binop == Iop_DivModS64to32); - HReg r_srcR = iselWordExpr_R(env, e->Iex.Binop.arg2); - - iselInt64Expr(&r_sHi, &r_sLo, env, e->Iex.Binop.arg1); - addInstr(env, MIPSInstr_Div(syned, True, r_sLo, r_srcR)); - addInstr(env, MIPSInstr_Mfhi(tHi)); - addInstr(env, MIPSInstr_Mflo(tLo)); - *rHi = tHi; - *rLo = tLo; - - return; - } case Iop_DivModU32to32: case Iop_DivModS32to32: { diff --git a/memcheck/tests/vbit-test/irops.c b/memcheck/tests/vbit-test/irops.c index 946e932..7f9ce90 100644 --- a/memcheck/tests/vbit-test/irops.c +++ b/memcheck/tests/vbit-test/irops.c @@ -144,12 +144,12 @@ static irop_t irops[] = { { DEFOP(Iop_DivS32E, UNDEF_ALL), .s390x = 0, .amd64 = 0, .x86 = 0, .arm = 0, .ppc64 = 1, .ppc32 = 1, .mips32 = 0, .mips64 = 0 }, // On s390 the DivMod operations always appear in a certain context // So they cannot be tested in isolation on that platform. - { DEFOP(Iop_DivModU64to32, UNDEF_ALL), .s390x = 0, .amd64 = 1, .x86 = 1, .arm = 0, .ppc64 = 0, .ppc32 = 0, .mips32 = 1, .mips64 = 1 }, - { DEFOP(Iop_DivModS64to32, UNDEF_ALL), .s390x = 0, .amd64 = 1, .x86 = 1, .arm = 0, .ppc64 = 0, .ppc32 = 0, .mips32 = 1, .mips64 = 1 }, + { DEFOP(Iop_DivModU64to32, UNDEF_ALL), .s390x = 0, .amd64 = 1, .x86 = 1, .arm = 0, .ppc64 = 0, .ppc32 = 0, .mips32 = 0, .mips64 = 0 }, + { DEFOP(Iop_DivModS64to32, UNDEF_ALL), .s390x = 0, .amd64 = 1, .x86 = 1, .arm = 0, .ppc64 = 0, .ppc32 = 0, .mips32 = 0, .mips64 = 0 }, { DEFOP(Iop_DivModU32to32, UNDEF_ALL), .s390x = 0, .amd64 = 0, .x86 = 0, .arm = 0, .ppc64 = 0, .ppc32 = 0, .mips32 = 1, .mips64 = 1 }, { DEFOP(Iop_DivModS32to32, UNDEF_ALL), .s390x = 0, .amd64 = 0, .x86 = 0, .arm = 0, .ppc64 = 0, .ppc32 = 0, .mips32 = 1, .mips64 = 1 }, - { DEFOP(Iop_DivModU128to64, UNDEF_ALL), .s390x = 0, .amd64 = 1, .x86 = 0, .arm = 0, .ppc64 = 0, .ppc32 = 0, .mips32 = 0, .mips64 = 1 }, // mips asserts - { DEFOP(Iop_DivModS128to64, UNDEF_ALL), .s390x = 0, .amd64 = 1, .x86 = 0, .arm = 0, .ppc64 = 0, .ppc32 = 0, .mips32 = 0, .mips64 = 1 }, // mips asserts + { DEFOP(Iop_DivModU128to64, UNDEF_ALL), .s390x = 0, .amd64 = 1, .x86 = 0, .arm = 0, .ppc64 = 0, .ppc32 = 0, .mips32 = 0, .mips64 = 0 }, // mips asserts + { DEFOP(Iop_DivModS128to64, UNDEF_ALL), .s390x = 0, .amd64 = 1, .x86 = 0, .arm = 0, .ppc64 = 0, .ppc32 = 0, .mips32 = 0, .mips64 = 0 }, // mips asserts { DEFOP(Iop_DivModS64to64, UNDEF_ALL), .s390x = 0, .amd64 = 0, .x86 = 0, .arm = 0, .ppc64 = 0, .ppc32 = 0, .mips32 = 0, .mips64 = 1 }, // mips asserts { DEFOP(Iop_DivModU64to64, UNDEF_ALL), .s390x = 0, .amd64 = 0, .x86 = 0, .arm = 0, .ppc64 = 0, .ppc32 = 0, .mips32 = 0, .mips64 = 1 }, // mips asserts { DEFOP(Iop_8Uto16, UNDEF_ZEXT), .s390x = 1, .amd64 = 1, .x86 = 1, .arm = 0, .ppc64 = 1, .ppc32 = 1, .mips32 = 1, .mips64 = 1 }, |
|
From: Petar J. <pe...@so...> - 2017-08-22 14:06:00
|
https://sourceware.org/git/gitweb.cgi?p=valgrind.git;h=5f3cc6c828a84683225c1ba14b4fc4c621c6b7d6 commit 5f3cc6c828a84683225c1ba14b4fc4c621c6b7d6 Author: Petar Jovanovic <mip...@gm...> Date: Tue Aug 22 15:53:15 2017 +0200 mips: reimplement handling of div, divu and ddivu Previous implementation misused some opcodes, and a side effect was dead code emission. To reimplement handling of these instructions, three new IoPs have been introduced: Iop_DivModU64to64, // :: I64,I64 -> I128 // of which lo half is div and hi half is mod Iop_DivModS32to32, // :: I32,I32 -> I64 // of which lo half is div and hi half is mod Iop_DivModU32to32, // :: I32,I32 -> I64 // of which lo half is div and hi half is mod Patch by Aleksandra Karadzic and Tamara Vlahovic. Diff: --- VEX/priv/guest_mips_toIR.c | 36 ++++++++++++++---------------- VEX/priv/host_mips_isel.c | 47 ++++++++++++++++++++++++++++++++++++++++ VEX/priv/ir_defs.c | 11 ++++++++-- VEX/pub/libvex_ir.h | 6 +++++ memcheck/mc_translate.c | 15 ++++++++++++- memcheck/tests/vbit-test/irops.c | 5 ++++- 6 files changed, 96 insertions(+), 24 deletions(-) diff --git a/VEX/priv/guest_mips_toIR.c b/VEX/priv/guest_mips_toIR.c index a73b5dc..d5215f1 100644 --- a/VEX/priv/guest_mips_toIR.c +++ b/VEX/priv/guest_mips_toIR.c @@ -15661,20 +15661,19 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, if (mode64) { t2 = newTemp(Ity_I64); - assign(t2, binop(Iop_DivModS64to32, - getIReg(rs), mkNarrowTo32(ty, getIReg(rt)))); + assign(t2, binop(Iop_DivModS32to32, + mkNarrowTo32(ty, getIReg(rs)), + mkNarrowTo32(ty, getIReg(rt)))); putHI(mkWidenFrom32(ty, unop(Iop_64HIto32, mkexpr(t2)), True)); putLO(mkWidenFrom32(ty, unop(Iop_64to32, mkexpr(t2)), True)); } else { t1 = newTemp(Ity_I64); - t2 = newTemp(Ity_I64); - assign(t1, unop(Iop_32Sto64, getIReg(rs))); - assign(t2, binop(Iop_DivModS64to32, mkexpr(t1), getIReg(rt))); + assign(t1, binop(Iop_DivModS32to32, getIReg(rs), getIReg(rt))); - putHI(unop(Iop_64HIto32, mkexpr(t2))); - putLO(unop(Iop_64to32, mkexpr(t2))); + putHI(unop(Iop_64HIto32, mkexpr(t1))); + putLO(unop(Iop_64to32, mkexpr(t1))); } break; @@ -15683,18 +15682,18 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, if (mode64) { t2 = newTemp(Ity_I64); - assign(t2, binop(Iop_DivModU64to32, - getIReg(rs), mkNarrowTo32(ty, getIReg(rt)))); + assign(t2, binop(Iop_DivModU32to32, + mkNarrowTo32(ty, getIReg(rs)), + mkNarrowTo32(ty, getIReg(rt)))); putHI(mkWidenFrom32(ty, unop(Iop_64HIto32, mkexpr(t2)), True)); putLO(mkWidenFrom32(ty, unop(Iop_64to32, mkexpr(t2)), True)); } else { t1 = newTemp(Ity_I64); - t2 = newTemp(Ity_I64); - assign(t1, unop(Iop_32Uto64, getIReg(rs))); - assign(t2, binop(Iop_DivModU64to32, mkexpr(t1), getIReg(rt))); - putHI(unop(Iop_64HIto32, mkexpr(t2))); - putLO(unop(Iop_64to32, mkexpr(t2))); + + assign(t1, binop(Iop_DivModU32to32, getIReg(rs), getIReg(rt))); + putHI(unop(Iop_64HIto32, mkexpr(t1))); + putLO(unop(Iop_64to32, mkexpr(t1))); } break; @@ -15731,14 +15730,11 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, case 0x1F: /* Doubleword Divide Unsigned DDIVU; MIPS64 check this */ DIP("ddivu r%u, r%u", rs, rt); t1 = newTemp(Ity_I128); - t2 = newTemp(Ity_I128); - assign(t1, binop(Iop_64HLto128, mkU64(0), getIReg(rs))); + assign(t1, binop(Iop_DivModU64to64, getIReg(rs), getIReg(rt))); - assign(t2, binop(Iop_DivModU128to64, mkexpr(t1), getIReg(rt))); - - putHI(unop(Iop_128HIto64, mkexpr(t2))); - putLO(unop(Iop_128to64, mkexpr(t2))); + putHI(unop(Iop_128HIto64, mkexpr(t1))); + putLO(unop(Iop_128to64, mkexpr(t1))); break; case 0x10: { /* MFHI */ diff --git a/VEX/priv/host_mips_isel.c b/VEX/priv/host_mips_isel.c index 0c51c07..2c49a6e 100644 --- a/VEX/priv/host_mips_isel.c +++ b/VEX/priv/host_mips_isel.c @@ -1218,6 +1218,36 @@ static HReg iselWordExpr_R_wrk(ISelEnv * env, IRExpr * e) return r_dst; } + if (e->Iex.Binop.op == Iop_DivModU32to32 || + e->Iex.Binop.op == Iop_DivModS32to32) { + HReg tLo = newVRegI(env); + HReg tHi = newVRegI(env); + HReg mask = newVRegI(env); + HReg tLo_1 = newVRegI(env); + HReg tHi_1 = newVRegI(env); + HReg r_dst = newVRegI(env); + Bool syned = toBool(e->Iex.Binop.op == Iop_DivModS32to32); + + HReg r_srcR = iselWordExpr_R(env, e->Iex.Binop.arg2); + HReg r_srcL = iselWordExpr_R(env, e->Iex.Binop.arg1); + + addInstr(env, MIPSInstr_Div(syned, True, r_srcL, r_srcR)); + addInstr(env, MIPSInstr_Mfhi(tHi)); + addInstr(env, MIPSInstr_Mflo(tLo)); + + addInstr(env, MIPSInstr_Shft(Mshft_SLL, False, tHi_1, tHi, + MIPSRH_Imm(False, 32))); + + addInstr(env, MIPSInstr_LI(mask, 0xffffffff)); + addInstr(env, MIPSInstr_Alu(Malu_AND, tLo_1, tLo, + MIPSRH_Reg(mask))); + + addInstr(env, MIPSInstr_Alu(Malu_OR, r_dst, tHi_1, + MIPSRH_Reg(tLo_1))); + + return r_dst; + } + if (e->Iex.Binop.op == Iop_8HLto16 || e->Iex.Binop.op == Iop_16HLto32) { HReg tHi = iselWordExpr_R(env, e->Iex.Binop.arg1); @@ -2216,6 +2246,7 @@ static void iselInt128Expr_wrk(HReg * rHi, HReg * rLo, ISelEnv * env, *rLo = iselWordExpr_R(env, e->Iex.Binop.arg2); return; + case Iop_DivModU64to64: case Iop_DivModS64to64: { HReg r_srcL = iselWordExpr_R(env, e->Iex.Binop.arg1); HReg r_srcR = iselWordExpr_R(env, e->Iex.Binop.arg2); @@ -2462,6 +2493,22 @@ static void iselInt64Expr_wrk(HReg * rHi, HReg * rLo, ISelEnv * env, IRExpr * e) return; } + case Iop_DivModU32to32: + case Iop_DivModS32to32: { + HReg r_srcL = iselWordExpr_R(env, e->Iex.Binop.arg1); + HReg r_srcR = iselWordExpr_R(env, e->Iex.Binop.arg2); + HReg tLo = newVRegI(env); + HReg tHi = newVRegI(env); + Bool syned = toBool(e->Iex.Binop.op == Iop_DivModS32to32); + + addInstr(env, MIPSInstr_Div(syned, True, r_srcL, r_srcR)); + addInstr(env, MIPSInstr_Mfhi(tHi)); + addInstr(env, MIPSInstr_Mflo(tLo)); + *rHi = tHi; + *rLo = tLo; + return; + } + /* 32HLto64(e1,e2) */ case Iop_32HLto64: *rHi = iselWordExpr_R(env, e->Iex.Binop.arg1); diff --git a/VEX/priv/ir_defs.c b/VEX/priv/ir_defs.c index a853c2e..8822800 100644 --- a/VEX/priv/ir_defs.c +++ b/VEX/priv/ir_defs.c @@ -236,10 +236,14 @@ void ppIROp ( IROp op ) case Iop_DivModU64to32: vex_printf("DivModU64to32"); return; case Iop_DivModS64to32: vex_printf("DivModS64to32"); return; + case Iop_DivModU32to32: vex_printf("DivModU32to32"); return; + case Iop_DivModS32to32: vex_printf("DivModS32to32"); return; + case Iop_DivModU128to64: vex_printf("DivModU128to64"); return; case Iop_DivModS128to64: vex_printf("DivModS128to64"); return; case Iop_DivModS64to64: vex_printf("DivModS64to64"); return; + case Iop_DivModU64to64: vex_printf("DivModU64to64"); return; case Iop_16HIto8: vex_printf("16HIto8"); return; case Iop_16to8: vex_printf("16to8"); return; @@ -2758,12 +2762,15 @@ void typeOfPrimop ( IROp op, BINARY(Ity_I64,Ity_I64, Ity_I64); case Iop_DivModU64to32: case Iop_DivModS64to32: - BINARY(Ity_I64,Ity_I32, Ity_I64); + BINARY(Ity_I64, Ity_I32, Ity_I64); + + case Iop_DivModU32to32: case Iop_DivModS32to32: + BINARY(Ity_I32, Ity_I32, Ity_I64); case Iop_DivModU128to64: case Iop_DivModS128to64: BINARY(Ity_I128,Ity_I64, Ity_I128); - case Iop_DivModS64to64: + case Iop_DivModU64to64: case Iop_DivModS64to64: BINARY(Ity_I64,Ity_I64, Ity_I128); case Iop_16HIto8: case Iop_16to8: diff --git a/VEX/pub/libvex_ir.h b/VEX/pub/libvex_ir.h index fcac043..57fa9b6 100644 --- a/VEX/pub/libvex_ir.h +++ b/VEX/pub/libvex_ir.h @@ -503,6 +503,12 @@ typedef Iop_DivModS64to64, // :: I64,I64 -> I128 // of which lo half is div and hi half is mod + Iop_DivModU64to64, // :: I64,I64 -> I128 + // of which lo half is div and hi half is mod + Iop_DivModS32to32, // :: I32,I32 -> I64 + // of which lo half is div and hi half is mod + Iop_DivModU32to32, // :: I32,I32 -> I64 + // of which lo half is div and hi half is mod /* Integer conversions. Some of these are redundant (eg Iop_64to8 is the same as Iop_64to32 and then Iop_32to8), but diff --git a/memcheck/mc_translate.c b/memcheck/mc_translate.c index 8429301..980c1d7 100644 --- a/memcheck/mc_translate.c +++ b/memcheck/mc_translate.c @@ -3977,7 +3977,13 @@ IRAtom* expr2vbits_Binop ( MCEnv* mce, case Iop_32HLto64: return assignNew('V', mce, Ity_I64, binop(op, vatom1, vatom2)); - case Iop_DivModS64to64: + case Iop_DivModU64to64: + case Iop_DivModS64to64: { + IRAtom* vTmp64 = mkLazy2(mce, Ity_I64, vatom1, vatom2); + return assignNew('V', mce, Ity_I128, + binop(Iop_64HLto128, vTmp64, vTmp64)); + } + case Iop_MullS64: case Iop_MullU64: { IRAtom* vLo64 = mkLeft64(mce, mkUifU64(mce, vatom1,vatom2)); @@ -3986,6 +3992,13 @@ IRAtom* expr2vbits_Binop ( MCEnv* mce, binop(Iop_64HLto128, vHi64, vLo64)); } + case Iop_DivModU32to32: + case Iop_DivModS32to32: { + IRAtom* vTmp32 = mkLazy2(mce, Ity_I32, vatom1, vatom2); + return assignNew('V', mce, Ity_I64, + binop(Iop_32HLto64, vTmp32, vTmp32)); + } + case Iop_MullS32: case Iop_MullU32: { IRAtom* vLo32 = mkLeft32(mce, mkUifU32(mce, vatom1,vatom2)); diff --git a/memcheck/tests/vbit-test/irops.c b/memcheck/tests/vbit-test/irops.c index d0e8298..946e932 100644 --- a/memcheck/tests/vbit-test/irops.c +++ b/memcheck/tests/vbit-test/irops.c @@ -146,9 +146,12 @@ static irop_t irops[] = { // So they cannot be tested in isolation on that platform. { DEFOP(Iop_DivModU64to32, UNDEF_ALL), .s390x = 0, .amd64 = 1, .x86 = 1, .arm = 0, .ppc64 = 0, .ppc32 = 0, .mips32 = 1, .mips64 = 1 }, { DEFOP(Iop_DivModS64to32, UNDEF_ALL), .s390x = 0, .amd64 = 1, .x86 = 1, .arm = 0, .ppc64 = 0, .ppc32 = 0, .mips32 = 1, .mips64 = 1 }, + { DEFOP(Iop_DivModU32to32, UNDEF_ALL), .s390x = 0, .amd64 = 0, .x86 = 0, .arm = 0, .ppc64 = 0, .ppc32 = 0, .mips32 = 1, .mips64 = 1 }, + { DEFOP(Iop_DivModS32to32, UNDEF_ALL), .s390x = 0, .amd64 = 0, .x86 = 0, .arm = 0, .ppc64 = 0, .ppc32 = 0, .mips32 = 1, .mips64 = 1 }, { DEFOP(Iop_DivModU128to64, UNDEF_ALL), .s390x = 0, .amd64 = 1, .x86 = 0, .arm = 0, .ppc64 = 0, .ppc32 = 0, .mips32 = 0, .mips64 = 1 }, // mips asserts { DEFOP(Iop_DivModS128to64, UNDEF_ALL), .s390x = 0, .amd64 = 1, .x86 = 0, .arm = 0, .ppc64 = 0, .ppc32 = 0, .mips32 = 0, .mips64 = 1 }, // mips asserts - { DEFOP(Iop_DivModS64to64, UNDEF_ALL), .s390x = 0, .amd64 = 0, .x86 = 0, .arm = 0, .ppc64 = 0, .ppc32 = 0, .mips32 = 0, .mips64 = 0 }, // mips asserts + { DEFOP(Iop_DivModS64to64, UNDEF_ALL), .s390x = 0, .amd64 = 0, .x86 = 0, .arm = 0, .ppc64 = 0, .ppc32 = 0, .mips32 = 0, .mips64 = 1 }, // mips asserts + { DEFOP(Iop_DivModU64to64, UNDEF_ALL), .s390x = 0, .amd64 = 0, .x86 = 0, .arm = 0, .ppc64 = 0, .ppc32 = 0, .mips32 = 0, .mips64 = 1 }, // mips asserts { DEFOP(Iop_8Uto16, UNDEF_ZEXT), .s390x = 1, .amd64 = 1, .x86 = 1, .arm = 0, .ppc64 = 1, .ppc32 = 1, .mips32 = 1, .mips64 = 1 }, { DEFOP(Iop_8Uto32, UNDEF_ZEXT), .s390x = 1, .amd64 = 1, .x86 = 1, .arm = 1, .ppc64 = 1, .ppc32 = 1, .mips32 = 1, .mips64 = 1 }, { DEFOP(Iop_8Uto64, UNDEF_ZEXT), .s390x = 1, .amd64 = 1, .x86 = 0, .arm = 0, .ppc64 = 1, .ppc32 = 0, .mips32 = 0, .mips64 = 1 }, // ppc32 assert |
|
From: Ivo R. <ir...@so...> - 2017-08-21 14:24:56
|
https://sourceware.org/git/gitweb.cgi?p=valgrind.git;h=ad1c929a82279b166679b65b1dfe93b63f0bf8fb commit ad1c929a82279b166679b65b1dfe93b63f0bf8fb Author: Ivo Raisr <iv...@iv...> Date: Fri Aug 18 16:53:57 2017 +0200 Recognize signal 151 (SIGLIBRT) sent by gdb. It has been observed that gdb on Solaris sends this signal to child processes. Unfortunately array "pass_signals" was too small to accomodate this signal and subsequently VG_(clo_vex_control).iropt_verbosity was overwritten. This has been fixed now. Diff: --- coregrind/m_gdbserver/gdb/signals.h | 2 ++ coregrind/m_gdbserver/signals.c | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/coregrind/m_gdbserver/gdb/signals.h b/coregrind/m_gdbserver/gdb/signals.h index c240f6b..1ad4a0a 100644 --- a/coregrind/m_gdbserver/gdb/signals.h +++ b/coregrind/m_gdbserver/gdb/signals.h @@ -229,6 +229,8 @@ enum target_signal TARGET_EXC_SOFTWARE, TARGET_EXC_BREAKPOINT, + TARGET_SIGNAL_LIBRT, + /* If you are adding a new signal, add it just above this comment. */ /* Last and unused enum value, for sizing arrays, etc. */ diff --git a/coregrind/m_gdbserver/signals.c b/coregrind/m_gdbserver/signals.c index ff5bfaf..69103ec 100644 --- a/coregrind/m_gdbserver/signals.c +++ b/coregrind/m_gdbserver/signals.c @@ -197,6 +197,8 @@ static struct { {"EXC_SOFTWARE", "Software generated exception"}, {"EXC_BREAKPOINT", "Breakpoint"}, + {"SIGLIBRT", "librt internal signal"}, + /* Last entry, used to check whether the table is the right size. */ {NULL, "TARGET_SIGNAL_MAGIC"} }; @@ -465,6 +467,10 @@ enum target_signal target_signal_from_host (int hostsig) if (hostsig == VKI_SIGINFO) return TARGET_SIGNAL_INFO; #endif +#if defined (VKI_SIGLIBRT) + if (hostsig == VKI_SIGLIBRT) + return TARGET_SIGNAL_LIBRT; +#endif #if defined (VKI_SIGRTMIN) if (hostsig >= VKI_SIGRTMIN && hostsig < VKI_SIGRTMAX) { @@ -714,6 +720,10 @@ int do_target_signal_to_host (enum target_signal oursig, case TARGET_SIGNAL_INFO: return VKI_SIGINFO; #endif +#if defined (SIGLIBRT) + case TARGET_SIGNAL_LIBRT: + return SIGLIBRT; +#endif default: #if defined (VKI_SIGRTMIN) |
|
From: John D. <je...@al...> - 2017-08-18 15:34:29
|
Looks good, thanks Ivo! On Fri, Aug 18, 2017 at 7:59 AM, Ivo Raisr <iv...@iv...> wrote: > 2017-08-16 3:19 GMT+02:00 John Davis <je...@al...>: > > Dear Valgrind developers, > > > > I've been writing a tool which uses Memcheck's xml output, and noticed > that > > it could generate two error types not listed in the documentation for the > > xml protocol. > > John, > > Thank you for the patch. It has been integrated as > commit 6c05c0edf42c76ccbc9180c848006adf4951cbf9: > https://sourceware.org/git/gitweb.cgi?p=valgrind.git;h= > 6c05c0edf42c76ccbc9180c848006adf4951cbf9 > > I. > |
|
From: Ivo R. <iv...@iv...> - 2017-08-18 14:59:17
|
Please have a look at the attached patch. Thank you, I. |
|
From: Ivo R. <iv...@iv...> - 2017-08-18 11:59:16
|
2017-08-16 3:19 GMT+02:00 John Davis <je...@al...>: > Dear Valgrind developers, > > I've been writing a tool which uses Memcheck's xml output, and noticed that > it could generate two error types not listed in the documentation for the > xml protocol. John, Thank you for the patch. It has been integrated as commit 6c05c0edf42c76ccbc9180c848006adf4951cbf9: https://sourceware.org/git/gitweb.cgi?p=valgrind.git;h=6c05c0edf42c76ccbc9180c848006adf4951cbf9 I. |
|
From: Ivo R. <ir...@so...> - 2017-08-18 11:52:35
|
https://sourceware.org/git/gitweb.cgi?p=valgrind.git;h=6c05c0edf42c76ccbc9180c848006adf4951cbf9 commit 6c05c0edf42c76ccbc9180c848006adf4951cbf9 Author: Ivo Raisr <iv...@iv...> Date: Thu Aug 17 10:18:21 2017 +0200 Add missing error kinds for Memcheck into XML output protocol 4. Error kinds have been re-ordered to match MC_ErrorTag. Patch by: John Davis <je...@al...> Diff: --- docs/internals/xml-output-protocol4.txt | 54 +++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/docs/internals/xml-output-protocol4.txt b/docs/internals/xml-output-protocol4.txt index 65c055d..3703bbd 100644 --- a/docs/internals/xml-output-protocol4.txt +++ b/docs/internals/xml-output-protocol4.txt @@ -9,9 +9,8 @@ are four variants of Protocol 4, one for each tool, since they produce different errors. The four variants differ only in the definition of the ERROR nonterminal and are otherwise identical. -NOTE that Protocol 4 (for the current svn trunk, which will eventually -become 3.7.x) is still under development. The text herein should not -be regarded as the final definition. +NOTE that Protocol 4 (for the current git master) is still under development. +The text herein should not be regarded as the final definition. Identification of Protocols @@ -498,14 +497,17 @@ KIND for Memcheck This is a small enumeration indicating roughly the nature of an error. The possible values are: - InvalidFree + UninitValue - free/delete/delete[] on an invalid pointer + other use of undefined value (primarily memory addresses) - MismatchedFree + UninitCondition - free/delete/delete[] does not match allocation function - (eg doing new[] then free on the result) + conditional jump/move depends on undefined value + + CoreMemError + + caused by unaddressable bytes in a signal handler frame InvalidRead @@ -519,30 +521,27 @@ The possible values are: jump to an invalid address - Overlap - - args overlap other otherwise bogus in eg memcpy - - InvalidMemPool + SyscallParam - invalid mem pool specified in client request + system call params are undefined or point to + undefined/unaddressible memory - UninitCondition + ClientCheck - conditional jump/move depends on undefined value + "error" resulting from a client check request - UninitValue + InvalidFree - other use of undefined value (primarily memory addresses) + free/delete/delete[] on an invalid pointer - SyscallParam + MismatchedFree - system call params are undefined or point to - undefined/unaddressible memory + free/delete/delete[] does not match allocation function + (eg doing new[] then free on the result) - ClientCheck + Overlap - "error" resulting from a client check request + args overlap other otherwise bogus in eg memcpy Leak_DefinitelyLost @@ -562,6 +561,15 @@ The possible values are: memory leak; pointers to un-freed blocks are still available + InvalidMemPool + + invalid mem pool specified in client request + + FishyValue + + arguments to memory allocation function are very large, + indicating probable integer overflow or cast from negative signed + ==================================================================== |
|
From: Ivo R. <ir...@so...> - 2017-08-17 20:40:51
|
https://sourceware.org/git/gitweb.cgi?p=valgrind.git;h=ba17add79a563bd1395dc05fa7309baffbcaa3ce commit ba17add79a563bd1395dc05fa7309baffbcaa3ce Author: Ivo Raisr <iv...@iv...> Date: Thu Aug 17 22:38:18 2017 +0200 Fix out-of-tree build on Solaris. Simply #include config.h with no path at all. Path by: Rob Boehne <ro...@da...> Diff: --- include/vki/vki-solaris.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/vki/vki-solaris.h b/include/vki/vki-solaris.h index 5b7bb21..5ab109c 100644 --- a/include/vki/vki-solaris.h +++ b/include/vki/vki-solaris.h @@ -42,7 +42,7 @@ #ifndef __VKI_SOLARIS_H #define __VKI_SOLARIS_H -#include "../../config.h" +#include "config.h" /* _XOPEN_SOURCE equal to at least '500' is required so that various system structures have all necessary attributes (for example struct msghdr). */ |
|
From: Petar J. <mip...@gm...> - 2017-08-17 18:12:19
|
Works like a charm. Thanks to everyone involved. Petar |
|
From: Petar J. <pe...@so...> - 2017-08-17 18:09:30
|
https://sourceware.org/git/gitweb.cgi?p=valgrind.git;h=b343cc638a058f325d766a91e14a4afd40b305e5 commit b343cc638a058f325d766a91e14a4afd40b305e5 Author: Petar Jovanovic <mip...@gm...> Date: Thu Aug 17 20:08:17 2017 +0200 mips32: finetune vfp test to avoid compiler warnings This patch removes two compiler warnings from the test: vfp.c: In function 'handler': vfp.c:260:4: warning: implicit declaration of function 'exit' [-Wimplicit-function-declaration] exit(0); ^ vfp.c:260:4: warning: incompatible implicit declaration of built-in function 'exit' vfp.c: At top level: vfp.c:258:13: warning: 'handler' defined but not used [-Wunused-function] static void handler(int sig) ^ Diff: --- none/tests/mips32/vfp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/none/tests/mips32/vfp.c b/none/tests/mips32/vfp.c index 9412457..3d03c02 100644 --- a/none/tests/mips32/vfp.c +++ b/none/tests/mips32/vfp.c @@ -3,6 +3,7 @@ #include <signal.h> #include <stdint.h> #include <stdio.h> +#include <stdlib.h> unsigned int mem[] = { 0x4095A266, 0x66666666, @@ -246,6 +247,7 @@ float mem1f[] = { out); \ } +#if (__mips==32) && (__mips_isa_rev>=2) && (__mips_fpr==64 || __mips_fpr==xx) #define TEST_FPU64 \ __asm__ __volatile__( \ "cvt.l.s $f0, $f0" "\n\t" \ @@ -258,6 +260,7 @@ static void handler(int sig) { exit(0); } +#endif void ppMem(double *m, int len) { |
|
From: Ivo R. <iv...@iv...> - 2017-08-17 08:22:40
|
2017-08-16 3:19 GMT+02:00 John Davis <je...@al...>: > Dear Valgrind developers, > > I've been writing a tool which uses Memcheck's xml output, and noticed that > it could generate two error types not listed in the documentation for the > xml protocol. This patch adds documentation for these error types (to the > best of my understanding of them). John, Thank you for the report. Please have a look at the attached patch. I. |