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
|
2
|
3
(2) |
4
|
5
|
6
|
|
7
|
8
|
9
|
10
|
11
(2) |
12
(1) |
13
(1) |
|
14
(1) |
15
(2) |
16
(2) |
17
(5) |
18
(2) |
19
(1) |
20
(1) |
|
21
|
22
|
23
(1) |
24
(3) |
25
|
26
|
27
|
|
28
|
29
(1) |
30
|
31
(1) |
|
|
|
|
From: Julian S. <se...@so...> - 2018-01-11 19:01:19
|
https://sourceware.org/git/gitweb.cgi?p=valgrind.git;h=cceed053ce876560b9a7512125dd93c7fa059778 commit cceed053ce876560b9a7512125dd93c7fa059778 Author: Julian Seward <js...@ac...> Date: Thu Jan 11 19:40:12 2018 +0100 Bug 79362 - Debug info is lost for .so files when they are dlclose'd. Majorly reworked by Philippe Waroquiers. Diff: --- .gitignore | 2 + NEWS | 6 + cachegrind/cg_main.c | 4 +- callgrind/bb.c | 3 +- callgrind/dump.c | 3 +- callgrind/fn.c | 10 +- coregrind/m_addrinfo.c | 22 +- coregrind/m_debuginfo/debuginfo.c | 442 ++++++++++++++++----- coregrind/m_debuginfo/priv_storage.h | 30 ++ coregrind/m_errormgr.c | 48 ++- coregrind/m_execontext.c | 72 +++- coregrind/m_gdbserver/m_gdbserver.c | 7 +- coregrind/m_gdbserver/server.c | 8 +- coregrind/m_gdbserver/target.c | 5 +- coregrind/m_gdbserver/valgrind-low-arm.c | 9 +- coregrind/m_gdbserver/valgrind-low-mips32.c | 6 +- coregrind/m_gdbserver/valgrind-low-mips64.c | 6 +- coregrind/m_libcassert.c | 2 +- coregrind/m_main.c | 8 +- coregrind/m_options.c | 1 + coregrind/m_redir.c | 7 +- coregrind/m_sbprofile.c | 9 +- coregrind/m_scheduler/scheduler.c | 8 +- coregrind/m_stacktrace.c | 26 +- coregrind/m_tooliface.c | 2 +- coregrind/m_translate.c | 18 +- coregrind/m_xtree.c | 42 +- coregrind/pub_core_debuginfo.h | 13 +- coregrind/pub_core_execontext.h | 6 + coregrind/pub_core_tooliface.h | 2 +- docs/xml/manual-core.xml | 22 + drd/drd_error.c | 3 +- exp-bbv/bbv_main.c | 5 +- exp-sgcheck/pc_common.c | 6 +- exp-sgcheck/sg_main.c | 3 +- helgrind/hg_addrdescr.c | 8 +- helgrind/hg_addrdescr.h | 4 +- helgrind/hg_errors.c | 12 +- helgrind/hg_main.c | 10 +- helgrind/libhb_core.c | 16 +- include/pub_tool_addrinfo.h | 11 +- include/pub_tool_basics.h | 18 + include/pub_tool_debuginfo.h | 48 ++- include/pub_tool_execontext.h | 14 +- include/pub_tool_options.h | 6 + include/pub_tool_stacktrace.h | 18 +- include/pub_tool_tooliface.h | 2 +- include/pub_tool_xtree.h | 2 +- lackey/lk_main.c | 3 +- massif/ms_main.c | 20 +- memcheck/mc_errors.c | 21 +- memcheck/mc_include.h | 4 +- memcheck/mc_leakcheck.c | 6 +- memcheck/mc_main.c | 6 +- memcheck/tests/linux/Makefile.am | 19 + .../tests/linux/dlclose_leak-no-keep.stderr.exp | 17 + .../tests/linux/dlclose_leak-no-keep.stdout.exp | 0 memcheck/tests/linux/dlclose_leak-no-keep.vgtest | 4 + memcheck/tests/linux/dlclose_leak.c | 32 ++ memcheck/tests/linux/dlclose_leak.stderr.exp | 17 + memcheck/tests/linux/dlclose_leak.stdout.exp | 0 memcheck/tests/linux/dlclose_leak.vgtest | 3 + memcheck/tests/linux/dlclose_leak_so.c | 21 + none/tests/cmdline1.stdout.exp | 4 + none/tests/cmdline2.stdout.exp | 4 + 65 files changed, 942 insertions(+), 274 deletions(-) diff --git a/.gitignore b/.gitignore index bb2cf9d..81bbc1d 100644 --- a/.gitignore +++ b/.gitignore @@ -1075,6 +1075,8 @@ /memcheck/tests/linux/.deps /memcheck/tests/linux/brk /memcheck/tests/linux/capget +/memcheck/tests/linux/dlclose_leak +/memcheck/tests/linux/dlclose_leak_so.so /memcheck/tests/linux/getregset /memcheck/tests/linux/ioctl-tiocsig /memcheck/tests/linux/lsframe1 diff --git a/NEWS b/NEWS index e27c3ea..46c4db2 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,11 @@ support for X86/macOS 10.13, AMD64/macOS 10.13. * ==================== CORE CHANGES =================== +* The new option --keep-debuginfo=no|yes (default no) can be used to + keep symbols etc for unloaded code. This allows saved stack traces + (e.g. memory leaks) to include file/line info for code that has been + dlclose'd (or similar). See user manual for more information and + known limitations. * ================== PLATFORM CHANGES ================= @@ -43,6 +48,7 @@ To see details of a given bug, visit https://bugs.kde.org/show_bug.cgi?id=XXXXXX where XXXXXX is the bug number as listed below. +79362 Debug info is lost for .so files when they are dlclose'd 208052 strlcpy error when n = 0 255603 exp-sgcheck Assertion '!already_present' failed 376257 helgrind history full speed up using a cached stack diff --git a/cachegrind/cg_main.c b/cachegrind/cg_main.c index 5d44667..cd4de76 100644 --- a/cachegrind/cg_main.c +++ b/cachegrind/cg_main.c @@ -210,12 +210,14 @@ static HChar* get_perm_string(const HChar* s) static void get_debug_info(Addr instr_addr, const HChar **dir, const HChar **file, const HChar **fn, UInt* line) { + DiEpoch ep = VG_(current_DiEpoch)(); Bool found_file_line = VG_(get_filename_linenum)( + ep, instr_addr, file, dir, line ); - Bool found_fn = VG_(get_fnname)(instr_addr, fn); + Bool found_fn = VG_(get_fnname)(ep, instr_addr, fn); if (!found_file_line) { *file = "???"; diff --git a/callgrind/bb.c b/callgrind/bb.c index b5459cf..06dd771 100644 --- a/callgrind/bb.c +++ b/callgrind/bb.c @@ -199,7 +199,8 @@ obj_node* obj_of_address(Addr addr) DebugInfo* di; PtrdiffT offset; - di = VG_(find_DebugInfo)(addr); + DiEpoch ep = VG_(current_DiEpoch)(); + di = VG_(find_DebugInfo)(ep, addr); obj = CLG_(get_obj_node)( di ); /* Update symbol offset in object if remapped */ diff --git a/callgrind/dump.c b/callgrind/dump.c index 1997c71..12ecf66 100644 --- a/callgrind/dump.c +++ b/callgrind/dump.c @@ -373,7 +373,8 @@ Bool get_debug_pos(BBCC* bbcc, Addr addr, AddrPos* p) found_file_line = debug_cache_info[cachepos]; } else { - found_file_line = VG_(get_filename_linenum)(addr, + DiEpoch ep = VG_(current_DiEpoch)(); + found_file_line = VG_(get_filename_linenum)(ep, addr, &file, &dir, &(p->line)); diff --git a/callgrind/fn.c b/callgrind/fn.c index d4c7b24..209c021 100644 --- a/callgrind/fn.c +++ b/callgrind/fn.c @@ -434,17 +434,18 @@ Bool CLG_(get_debug_info)(Addr instr_addr, CLG_DEBUG(6, " + get_debug_info(%#lx)\n", instr_addr); + DiEpoch ep = VG_(current_DiEpoch)(); if (pDebugInfo) { - *pDebugInfo = VG_(find_DebugInfo)(instr_addr); + *pDebugInfo = VG_(find_DebugInfo)(ep, instr_addr); // for generated code in anonymous space, pSegInfo is 0 } - found_file_line = VG_(get_filename_linenum)(instr_addr, + found_file_line = VG_(get_filename_linenum)(ep, instr_addr, file, dir, &line); - found_fn = VG_(get_fnname)(instr_addr, fn_name); + found_fn = VG_(get_fnname)(ep, instr_addr, fn_name); if (!found_file_line && !found_fn) { CLG_(stat).no_debug_BBs++; @@ -503,6 +504,7 @@ fn_node* CLG_(get_fn_node)(BB* bb) CLG_(get_debug_info)(bb_addr(bb), &dirname, &filename, &fnname, &line_num, &di); + DiEpoch ep = VG_(current_DiEpoch)(); if (0 == VG_(strcmp)(fnname, "???")) { int p; static HChar buf[32]; // for sure large enough @@ -521,7 +523,7 @@ fn_node* CLG_(get_fn_node)(BB* bb) fnname = buf; } else { - if (VG_(get_fnname_if_entry)(bb_addr(bb), &fnname)) + if (VG_(get_fnname_if_entry)(ep, bb_addr(bb), &fnname)) bb->is_entry = 1; } diff --git a/coregrind/m_addrinfo.c b/coregrind/m_addrinfo.c index c3edc13..d22a99e 100644 --- a/coregrind/m_addrinfo.c +++ b/coregrind/m_addrinfo.c @@ -86,7 +86,7 @@ static ThreadId find_tid_with_stack_containing (Addr a) return VG_INVALID_THREADID; } -void VG_(describe_addr) ( Addr a, /*OUT*/AddrInfo* ai ) +void VG_(describe_addr) ( DiEpoch ep, Addr a, /*OUT*/AddrInfo* ai ) { VgSectKind sect; @@ -99,7 +99,7 @@ void VG_(describe_addr) ( Addr a, /*OUT*/AddrInfo* ai ) VG_(free), sizeof(HChar) ); (void) VG_(get_data_description)( ai->Addr.Variable.descr1, - ai->Addr.Variable.descr2, a ); + ai->Addr.Variable.descr2, ep, a ); /* If there's nothing in descr1/2, free them. Why is it safe to VG_(indexXA) at zero here? Because VG_(get_data_description) guarantees to zero terminate descr1/2 regardless of the outcome @@ -127,7 +127,7 @@ void VG_(describe_addr) ( Addr a, /*OUT*/AddrInfo* ai ) there. -- */ const HChar *name; if (VG_(get_datasym_and_offset)( - a, &name, + ep, a, &name, &ai->Addr.DataSym.offset )) { ai->Addr.DataSym.name = VG_(strdup)("mc.da.dsname", name); ai->tag = Addr_DataSym; @@ -148,6 +148,7 @@ void VG_(describe_addr) ( Addr a, /*OUT*/AddrInfo* ai ) ai->tag = Addr_Stack; VG_(initThreadInfo)(&ai->Addr.Stack.tinfo); ai->Addr.Stack.tinfo.tid = tid; + ai->Addr.Stack.epoch = ep; ai->Addr.Stack.IP = 0; ai->Addr.Stack.frameNo = -1; ai->Addr.Stack.stackPos = StackPos_stacked; @@ -248,6 +249,7 @@ void VG_(describe_addr) ( Addr a, /*OUT*/AddrInfo* ai ) ai->tag = Addr_Stack; VG_(initThreadInfo)(&ai->Addr.Stack.tinfo); ai->Addr.Stack.tinfo.tid = tid; + ai->Addr.Stack.epoch = ep; ai->Addr.Stack.IP = 0; ai->Addr.Stack.frameNo = -1; vg_assert (stackPos != StackPos_stacked); @@ -447,20 +449,24 @@ static void pp_addrinfo_WRK ( Addr a, const AddrInfo* ai, Bool mc, Bool haslinenum; PtrdiffT offset; - if (VG_(get_inst_offset_in_function)( ai->Addr.Stack.IP, + if (VG_(get_inst_offset_in_function)( ai->Addr.Stack.epoch, + ai->Addr.Stack.IP, &offset)) - haslinenum = VG_(get_linenum) (ai->Addr.Stack.IP - offset, + haslinenum = VG_(get_linenum) (ai->Addr.Stack.epoch, + ai->Addr.Stack.IP - offset, &linenum); else haslinenum = False; - hasfile = VG_(get_filename)(ai->Addr.Stack.IP, &file); + hasfile = VG_(get_filename)(ai->Addr.Stack.epoch, + ai->Addr.Stack.IP, &file); HChar strlinenum[16] = ""; // large enough if (hasfile && haslinenum) VG_(sprintf)(strlinenum, "%u", linenum); - hasfn = VG_(get_fnname)(ai->Addr.Stack.IP, &fn); + hasfn = VG_(get_fnname)(ai->Addr.Stack.epoch, + ai->Addr.Stack.IP, &fn); if (hasfn || hasfile) VG_(emit)( "%sin frame #%d, created by %ps (%ps:%s)%s\n", @@ -603,7 +609,7 @@ static void pp_addrinfo_WRK ( Addr a, const AddrInfo* ai, Bool mc, if (ai->Addr.SectKind.kind == Vg_SectText) { /* To better describe the address in a text segment, pp a dummy stacktrace made of this single address. */ - VG_(pp_StackTrace)( &a, 1 ); + VG_(pp_StackTrace)( VG_(current_DiEpoch)(), &a, 1 ); } break; diff --git a/coregrind/m_debuginfo/debuginfo.c b/coregrind/m_debuginfo/debuginfo.c index e5ce4be..d481458 100644 --- a/coregrind/m_debuginfo/debuginfo.c +++ b/coregrind/m_debuginfo/debuginfo.c @@ -47,6 +47,7 @@ #include "pub_core_machine.h" // VG_PLAT_USES_PPCTOC #include "pub_core_xarray.h" #include "pub_core_oset.h" +#include "pub_core_execontext.h" #include "pub_core_stacktrace.h" // VG_(get_StackTrace) XXX: circular dependency #include "pub_core_ume.h" @@ -70,6 +71,10 @@ should-we-load-debuginfo-now? finite state machine. */ #define DEBUG_FSM 0 +/* Set this to 1 to enable somewhat minimal debug printing for the + debuginfo-epoch machinery. */ +#define DEBUG_EPOCHS 0 + /*------------------------------------------------------------*/ /*--- The _svma / _avma / _image / _bias naming scheme ---*/ @@ -109,6 +114,110 @@ static void caches__invalidate (void); /*------------------------------------------------------------*/ +/*--- Epochs ---*/ +/*------------------------------------------------------------*/ + +/* The DebugInfo epoch is incremented every time we either load debuginfo in + response to an object mapping, or an existing DebugInfo becomes + non-current (or will be discarded) due to an object unmap. By storing, + in each DebugInfo, the first and last epoch for which it is valid, we can + unambiguously identify the set of DebugInfos which should be used to + provide metadata for a code or data address, provided we know the epoch + to which that address pertains. + + Note, this isn't the same as the "handle_counter" below. That only + advances when new DebugInfos are created. "current_epoch" advances both + at DebugInfo created and destruction-or-making-non-current. +*/ + +// The value zero is reserved for indicating an invalid epoch number. +static UInt current_epoch = 1; + +inline DiEpoch VG_(current_DiEpoch) ( void ) { + DiEpoch dep; dep.n = current_epoch; return dep; +} + +static void advance_current_DiEpoch ( const HChar* msg ) { + current_epoch++; + if (DEBUG_EPOCHS) + VG_(printf)("Advancing current epoch to %u due to %s\n", + current_epoch, msg); +} + +static inline Bool eq_DiEpoch ( DiEpoch dep1, DiEpoch dep2 ) { + return dep1.n == dep2.n && /*neither is invalid*/dep1.n != 0; +} + +// Is this DebugInfo currently "allocated" (pre-use state, only FSM active) ? +static inline Bool is_DebugInfo_allocated ( const DebugInfo* di ) +{ + if (is_DiEpoch_INVALID(di->first_epoch) + && is_DiEpoch_INVALID(di->last_epoch)) { + return True; + } else { + return False; + } +} + +// Is this DebugInfo currently "active" (valid for the current epoch) ? +static inline Bool is_DebugInfo_active ( const DebugInfo* di ) +{ + if (!is_DiEpoch_INVALID(di->first_epoch) + && is_DiEpoch_INVALID(di->last_epoch)) { + // Yes it is active. Sanity check .. + vg_assert(di->first_epoch.n <= current_epoch); + return True; + } else { + return False; + } +} + +// Is this DebugInfo currently "archived" ? +static inline Bool is_DebugInfo_archived ( const DebugInfo* di ) +{ + if (!is_DiEpoch_INVALID(di->first_epoch) + && !is_DiEpoch_INVALID(di->last_epoch)) { + // Yes it is archived. Sanity checks .. + vg_assert(di->first_epoch.n <= di->last_epoch.n); + vg_assert(di->last_epoch.n <= current_epoch); + return True; + } else { + return False; + } +} + +// Is this DebugInfo valid for the specified epoch? +static inline Bool is_DI_valid_for_epoch ( const DebugInfo* di, DiEpoch ep ) +{ + // Stay sane + vg_assert(ep.n > 0 && ep.n <= current_epoch); + + Bool first_valid = !is_DiEpoch_INVALID(di->first_epoch); + Bool last_valid = !is_DiEpoch_INVALID(di->last_epoch); + + if (first_valid) { + if (last_valid) { + // Both valid. di is in Archived state. + return di->first_epoch.n <= ep.n && ep.n <= di->last_epoch.n; + } else { + // First is valid, last is invalid. di is in Active state. + return di->first_epoch.n <= ep.n; + } + } else { + vg_assert (!last_valid); // First invalid, last valid is a bad state. + // Neither is valid. di is in Allocated state. + return False; + } + +} + +static inline UInt ROL32 ( UInt x, UInt n ) +{ + return (x << n) | (x >> (32-n)); +} + + +/*------------------------------------------------------------*/ /*--- Root structure ---*/ /*------------------------------------------------------------*/ @@ -162,6 +271,23 @@ static void move_DebugInfo_one_step_forward ( DebugInfo* di ) } +// Debugging helper for epochs +static void show_epochs ( const HChar* msg ) +{ + if (DEBUG_EPOCHS) { + DebugInfo* di; + VG_(printf)("\nDebugInfo epoch display, requested by \"%s\"\n", msg); + VG_(printf)(" Current epoch (note: 0 means \"invalid epoch\") = %u\n", + current_epoch); + for (di = debugInfo_list; di; di = di->next) { + VG_(printf)(" [di=%p] first %u last %u %s\n", + di, di->first_epoch.n, di->last_epoch.n, di->fsm.filename); + } + VG_(printf)("\n"); + } +} + + /*------------------------------------------------------------*/ /*--- Notification (acquire/discard) helpers ---*/ /*------------------------------------------------------------*/ @@ -182,6 +308,8 @@ DebugInfo* alloc_DebugInfo( const HChar* filename ) di = ML_(dinfo_zalloc)("di.debuginfo.aDI.1", sizeof(DebugInfo)); di->handle = handle_counter++; + di->first_epoch = DiEpoch_INVALID(); + di->last_epoch = DiEpoch_INVALID(); di->fsm.filename = ML_(dinfo_strdup)("di.debuginfo.aDI.2", filename); di->fsm.maps = VG_(newXA)( ML_(dinfo_zalloc), "di.debuginfo.aDI.3", @@ -302,34 +430,54 @@ static void free_DebugInfo ( DebugInfo* di ) } -/* 'si' is a member of debugInfo_list. Find it, remove it from the - list, notify m_redir that this has happened, and free all storage - reachable from it. +/* 'di' is a member of debugInfo_list. Find it, and either (remove it from + the list and free all storage reachable from it) or archive it. + Notify m_redir that this removal/archiving has happened. + + Note that 'di' can't be archived. Is a DebugInfo is archived then we + want to hold on to it forever. This is asserted for. + + Note also, we don't advance the current epoch here. That's the + responsibility of some (non-immediate) caller. */ -static void discard_DebugInfo ( DebugInfo* di ) +static void discard_or_archive_DebugInfo ( DebugInfo* di ) { - const HChar* reason = "munmap"; + const HChar* reason = "munmap"; + const Bool archive = VG_(clo_keep_debuginfo); DebugInfo** prev_next_ptr = &debugInfo_list; DebugInfo* curr = debugInfo_list; + /* It must be active! */ + vg_assert( is_DebugInfo_active(di)); while (curr) { if (curr == di) { - /* Found it; remove from list and free it. */ + /* Found it; (remove from list and free it), or archive it. */ if (curr->have_dinfo && (VG_(clo_verbosity) > 1 || VG_(clo_trace_redir))) VG_(message)(Vg_DebugMsg, - "Discarding syms at %#lx-%#lx in %s due to %s()\n", + "%s syms at %#lx-%#lx in %s due to %s()\n", + archive ? "Archiving" : "Discarding", di->text_avma, di->text_avma + di->text_size, curr->fsm.filename ? curr->fsm.filename : "???", reason); vg_assert(*prev_next_ptr == curr); - *prev_next_ptr = curr->next; - if (curr->have_dinfo) + if (!archive) { + *prev_next_ptr = curr->next; + } + if (curr->have_dinfo) { VG_(redir_notify_delete_DebugInfo)( curr ); - free_DebugInfo(curr); + } + if (archive) { + /* Adjust the epoch markers appropriately. */ + di->last_epoch = VG_(current_DiEpoch)(); + VG_(archive_ExeContext_in_range) (di->last_epoch, + di->text_avma, di->text_size); + } else { + free_DebugInfo(curr); + } return; } prev_next_ptr = &curr->next; @@ -358,10 +506,12 @@ static Bool discard_syms_in_range ( Addr start, SizeT length ) while (True) { if (curr == NULL) break; - if (curr->text_present - && curr->text_size > 0 - && (start+length - 1 < curr->text_avma - || curr->text_avma + curr->text_size - 1 < start)) { + if (is_DebugInfo_archived(curr) + || !curr->text_present + || (curr->text_present + && curr->text_size > 0 + && (start+length - 1 < curr->text_avma + || curr->text_avma + curr->text_size - 1 < start))) { /* no overlap */ } else { found = True; @@ -372,7 +522,7 @@ static Bool discard_syms_in_range ( Addr start, SizeT length ) if (!found) break; anyFound = True; - discard_DebugInfo( curr ); + discard_or_archive_DebugInfo( curr ); } return anyFound; @@ -418,9 +568,9 @@ static Bool do_DebugInfos_overlap ( const DebugInfo* di1, const DebugInfo* di2 ) } -/* Discard all elements of debugInfo_list whose .mark bit is set. +/* Discard or archive all elements of debugInfo_list whose .mark bit is set. */ -static void discard_marked_DebugInfos ( void ) +static void discard_or_archive_marked_DebugInfos ( void ) { DebugInfo* curr; @@ -436,7 +586,7 @@ static void discard_marked_DebugInfos ( void ) } if (!curr) break; - discard_DebugInfo( curr ); + discard_or_archive_DebugInfo( curr ); } } @@ -446,19 +596,22 @@ static void discard_marked_DebugInfos ( void ) Clearly diRef must have its mapping information set to something sane. */ static void discard_DebugInfos_which_overlap_with ( DebugInfo* diRef ) { + vg_assert(is_DebugInfo_allocated(diRef)); DebugInfo* di; /* Mark all the DebugInfos in debugInfo_list that need to be deleted. First, clear all the mark bits; then set them if they overlap with siRef. Since siRef itself is in this list we at least expect its own mark bit to be set. */ for (di = debugInfo_list; di; di = di->next) { + if (is_DebugInfo_archived(di)) + continue; di->mark = do_DebugInfos_overlap( di, diRef ); if (di == diRef) { vg_assert(di->mark); di->mark = False; } } - discard_marked_DebugInfos(); + discard_or_archive_marked_DebugInfos(); } @@ -470,6 +623,8 @@ static DebugInfo* find_or_create_DebugInfo_for ( const HChar* filename ) DebugInfo* di; vg_assert(filename); for (di = debugInfo_list; di; di = di->next) { + if (is_DebugInfo_archived(di)) + continue; vg_assert(di->fsm.filename); if (0==VG_(strcmp)(di->fsm.filename, filename)) break; @@ -480,6 +635,7 @@ static DebugInfo* find_or_create_DebugInfo_for ( const HChar* filename ) di->next = debugInfo_list; debugInfo_list = di; } + vg_assert(!is_DebugInfo_archived(di)); return di; } @@ -723,6 +879,8 @@ static ULong di_notify_ACHIEVE_ACCEPT_STATE ( struct _DebugInfo* di ) ULong di_handle; Bool ok; + advance_current_DiEpoch("di_notify_ACHIEVE_ACCEPT_STATE"); + vg_assert(di->fsm.filename); TRACE_SYMTAB("\n"); TRACE_SYMTAB("------ start ELF OBJECT " @@ -734,7 +892,8 @@ static ULong di_notify_ACHIEVE_ACCEPT_STATE ( struct _DebugInfo* di ) /* We're going to read symbols and debug info for the avma ranges specified in the _DebugInfoFsm mapping array. First get rid of any other DebugInfos which overlap any of those - ranges (to avoid total confusion). */ + ranges (to avoid total confusion). But only those valid in + the current epoch. We don't want to discard archived DebugInfos. */ discard_DebugInfos_which_overlap_with( di ); /* The DebugInfoMappings that now exist in the FSM may involve @@ -765,6 +924,15 @@ static ULong di_notify_ACHIEVE_ACCEPT_STATE ( struct _DebugInfo* di ) priv_storage.h. */ check_CFSI_related_invariants(di); ML_(finish_CFSI_arrays)(di); + + // Mark di's first epoch point as a valid epoch. Because its + // last_epoch value is still invalid, this changes di's state from + // "allocated" to "active". + vg_assert(is_DebugInfo_allocated(di)); + di->first_epoch = VG_(current_DiEpoch)(); + vg_assert(is_DebugInfo_active(di)); + show_epochs("di_notify_ACHIEVE_ACCEPT_STATE success"); + /* notify m_redir about it */ TRACE_SYMTAB("\n------ Notifying m_redir ------\n"); VG_(redir_notify_new_DebugInfo)( di ); @@ -1077,8 +1245,11 @@ void VG_(di_notify_munmap)( Addr a, SizeT len ) Bool anyFound; if (0) VG_(printf)("DISCARD %#lx %#lx\n", a, a+len); anyFound = discard_syms_in_range(a, len); - if (anyFound) + if (anyFound) { caches__invalidate(); + advance_current_DiEpoch("VG_(di_notify_munmap)"); + show_epochs("VG_(di_notify_munmap)"); + } } @@ -1094,8 +1265,10 @@ void VG_(di_notify_mprotect)( Addr a, SizeT len, UInt prot ) # endif if (0 && !exe_ok) { Bool anyFound = discard_syms_in_range(a, len); - if (anyFound) + if (anyFound) { caches__invalidate(); + advance_current_DiEpoch("VG_(di_notify_mprotect)"); + } } } @@ -1395,6 +1568,7 @@ void VG_(di_notify_pdb_debuginfo)( Int fd_obj, Addr avma_obj, caches__invalidate(); /* dump old info for this range, if any */ discard_syms_in_range( avma_obj, total_size ); + advance_current_DiEpoch("VG_(di_notify_pdb_debuginfo)"); { DebugInfo* di = find_or_create_DebugInfo_for(exename); @@ -1415,7 +1589,14 @@ void VG_(di_notify_pdb_debuginfo)( Int fd_obj, Addr avma_obj, } else { VG_(message)(Vg_UserMsg, "LOAD_PDB_DEBUGINFO: failed loading info " "from %s\n", pdbname); - discard_DebugInfo (di); + /* We cannot make any sense of this pdb, so (force) discard it, + even if VG_(clo_keep_debuginfo) is True. */ + const Bool save_clo_keep_debuginfo = VG_(clo_keep_debuginfo); + VG_(clo_keep_debuginfo) = False; + // The below will assert if di is not active. Not too sure what + // the state of di in this failed loading state. + discard_or_archive_DebugInfo (di); + VG_(clo_keep_debuginfo) = save_clo_keep_debuginfo; } VG_(am_munmap_valgrind)( (Addr)pdbimage, n_pdbimage ); VG_(close)(fd_pdbimage); @@ -1474,6 +1655,7 @@ DebugInfoMapping* ML_(find_rx_mapping) ( DebugInfo* di, Addr lo, Addr hi ) /*------------------------------------------------------------*/ /*--- Types and functions for inlined IP cursor ---*/ /*------------------------------------------------------------*/ + struct _InlIPCursor { Addr eip; // Cursor used to describe calls at eip. DebugInfo* di; // DebugInfo describing inlined calls at eip @@ -1537,8 +1719,8 @@ Bool VG_(next_IIPC)(InlIPCursor *iipc) } /* Forward */ -static void search_all_loctabs ( Addr ptr, /*OUT*/DebugInfo** pdi, - /*OUT*/Word* locno ); +static void search_all_loctabs ( DiEpoch ep, Addr ptr, + /*OUT*/DebugInfo** pdi, /*OUT*/Word* locno ); /* Returns the position after which eip would be inserted in inltab. (-1 if eip should be inserted before position 0). @@ -1568,7 +1750,7 @@ static Word inltab_insert_pos (DebugInfo *di, Addr eip) return lo - 1; } -InlIPCursor* VG_(new_IIPC)(Addr eip) +InlIPCursor* VG_(new_IIPC)(DiEpoch ep, Addr eip) { DebugInfo* di; Word locno; @@ -1579,8 +1761,8 @@ InlIPCursor* VG_(new_IIPC)(Addr eip) if (!VG_(clo_read_inline_info)) return NULL; // No way we can find inlined calls. - /* Search the DebugInfo for eip */ - search_all_loctabs ( eip, &di, &locno ); + /* Search the DebugInfo for (ep, eip) */ + search_all_loctabs ( ep, eip, &di, &locno ); if (di == NULL || di->inltab_used == 0) return NULL; // No di (with inltab) containing eip. @@ -1644,8 +1826,8 @@ void VG_(delete_IIPC)(InlIPCursor *iipc) If findText==True, only text symbols are searched for. If findText==False, only data symbols are searched for. */ -static void search_all_symtabs ( Addr ptr, /*OUT*/DebugInfo** pdi, - /*OUT*/Word* symno, +static void search_all_symtabs ( DiEpoch ep, Addr ptr, + /*OUT*/DebugInfo** pdi, /*OUT*/Word* symno, Bool findText ) { Word sno; @@ -1654,6 +1836,9 @@ static void search_all_symtabs ( Addr ptr, /*OUT*/DebugInfo** pdi, for (di = debugInfo_list; di != NULL; di = di->next) { + if (!is_DI_valid_for_epoch(di, ep)) + continue; + if (findText) { /* Consider any symbol in the r-x mapped area to be text. See Comment_Regarding_Text_Range_Checks in storage.c for @@ -1701,15 +1886,17 @@ static void search_all_symtabs ( Addr ptr, /*OUT*/DebugInfo** pdi, } -/* Search all loctabs that we know about to locate ptr. If found, set - *pdi to the relevant DebugInfo, and *locno to the loctab entry +/* Search all loctabs that we know about to locate ptr at epoch ep. If + *found, set pdi to the relevant DebugInfo, and *locno to the loctab entry *number within that. If not found, *pdi is set to NULL. */ -static void search_all_loctabs ( Addr ptr, /*OUT*/DebugInfo** pdi, - /*OUT*/Word* locno ) +static void search_all_loctabs ( DiEpoch ep, Addr ptr, + /*OUT*/DebugInfo** pdi, /*OUT*/Word* locno ) { Word lno; DebugInfo* di; for (di = debugInfo_list; di != NULL; di = di->next) { + if (!is_DI_valid_for_epoch(di, ep)) + continue; if (di->text_present && di->text_size > 0 && di->text_avma <= ptr @@ -1732,19 +1919,22 @@ static void search_all_loctabs ( Addr ptr, /*OUT*/DebugInfo** pdi, typedef struct { - Addr sym_avma; + // (sym_epoch, sym_avma) are the hash table key. + DiEpoch sym_epoch; + Addr sym_avma; + // Fields below here are not part of the key. const HChar* sym_name; PtrdiffT offset : (sizeof(PtrdiffT)*8)-1; Bool isText : 1; } Sym_Name_CacheEnt; -/* Sym_Name_CacheEnt associates a queried address to the sym name found. - By nature, if a sym name was found, it means the searched address - stored in the cache is an avma (see e.g. search_all_symtabs). - Note however that the caller is responsibe to work with 'avma' - addresses e.g. when calling VG_(get_fnname) : m_debuginfo.c has - no way to differentiate an 'svma a' from an 'avma a'. It is however - unlikely that svma would percolate outside of this module. */ +/* Sym_Name_CacheEnt associates a queried (epoch, address) pair to the sym + name found. By nature, if a sym name was found, it means the searched + address stored in the cache is an avma (see e.g. search_all_symtabs). + Note however that the caller is responsible to work with 'avma' addresses + e.g. when calling VG_(get_fnname) : m_debuginfo.c has no way to + differentiate an 'svma a' from an 'avma a'. It is however unlikely that + svma would percolate outside of this module. */ static Sym_Name_CacheEnt sym_name_cache[N_SYM_NAME_CACHE]; @@ -1760,13 +1950,15 @@ static void sym_name_cache__invalidate ( void ) { sym_name_cache[0].sym_name = no_sym_name; } -/* The whole point of this whole big deal: map a code address to a - plausible symbol name. Returns False if no idea; otherwise True. +/* The whole point of this whole big deal: map an (epoch, code address) pair + to a plausible symbol name. Returns False if no idea; otherwise True. + Caller supplies buf. If do_cxx_demangling is False, don't do C++ demangling, regardless of VG_(clo_demangle) -- probably because the call has come from VG_(get_fnname_raw)(). findText indicates whether we're looking for a text symbol or a data symbol -- caller must choose one kind or the other. + NOTE: See IMPORTANT COMMENT above about persistence and ownership in pub_tool_debuginfo.h get_sym_name and the fact it calls the demangler is the main reason @@ -1775,22 +1967,32 @@ static void sym_name_cache__invalidate ( void ) { (1) the DebugInfo it belongs to is not discarded (2) the demangler is not invoked again Also, the returned string is owned by "somebody else". Callers must - not free it or modify it.*/ + not free it or modify it. */ static Bool get_sym_name ( Bool do_cxx_demangling, Bool do_z_demangling, Bool do_below_main_renaming, - Addr a, const HChar** buf, + DiEpoch ep, Addr a, const HChar** buf, Bool match_anywhere_in_sym, Bool show_offset, Bool findText, /*OUT*/PtrdiffT* offsetP ) { - UWord hash = a % N_SYM_NAME_CACHE; - Sym_Name_CacheEnt* se = &sym_name_cache[hash]; - - if (UNLIKELY(se->sym_avma != a || se->isText != findText)) { + // Compute the hash from 'ep' and 'a'. The latter contains lots of + // significant bits, but 'ep' is expected to be a small number, typically + // less than 500. So rotate it around a bit in the hope of spreading the + // bits out somewhat. + vg_assert(!is_DiEpoch_INVALID(ep)); + UWord hash = a ^ (UWord)(ep.n ^ ROL32(ep.n, 5) + ^ ROL32(ep.n, 13) ^ ROL32(ep.n, 19)); + hash %= N_SYM_NAME_CACHE; + + Sym_Name_CacheEnt* se = &sym_name_cache[hash]; + + if (UNLIKELY(se->sym_epoch.n != ep.n || se->sym_avma != a + || se->isText != findText)) { DebugInfo* di; Word sno; - search_all_symtabs ( a, &di, &sno, findText ); + search_all_symtabs ( ep, a, &di, &sno, findText ); + se->sym_epoch = ep; se->sym_avma = a; se->isText = findText; if (di == NULL || a == 0) @@ -1849,12 +2051,12 @@ Bool get_sym_name ( Bool do_cxx_demangling, Bool do_z_demangling, /* ppc64be-linux only: find the TOC pointer (R2 value) that should be in force at the entry point address of the function containing guest_code_addr. Returns 0 if not known. */ -Addr VG_(get_tocptr) ( Addr guest_code_addr ) +Addr VG_(get_tocptr) ( DiEpoch ep, Addr guest_code_addr ) { #if defined(VGA_ppc64be) || defined(VGA_ppc64le) DebugInfo* si; Word sno; - search_all_symtabs ( guest_code_addr, + search_all_symtabs ( ep, guest_code_addr, &si, &sno, True/*consider text symbols only*/ ); if (si == NULL) @@ -1870,11 +2072,11 @@ Addr VG_(get_tocptr) ( Addr guest_code_addr ) match anywhere in function, but don't show offsets. NOTE: See IMPORTANT COMMENT above about persistence and ownership in pub_tool_debuginfo.h */ -Bool VG_(get_fnname) ( Addr a, const HChar** buf ) +Bool VG_(get_fnname) ( DiEpoch ep, Addr a, const HChar** buf ) { return get_sym_name ( /*C++-demangle*/True, /*Z-demangle*/True, /*below-main-renaming*/True, - a, buf, + ep, a, buf, /*match_anywhere_in_fun*/True, /*show offset?*/False, /*text sym*/True, @@ -1885,11 +2087,11 @@ Bool VG_(get_fnname) ( Addr a, const HChar** buf ) match anywhere in function, and show offset if nonzero. NOTE: See IMPORTANT COMMENT above about persistence and ownership in pub_tool_debuginfo.h */ -Bool VG_(get_fnname_w_offset) ( Addr a, const HChar** buf ) +Bool VG_(get_fnname_w_offset) ( DiEpoch ep, Addr a, const HChar** buf ) { return get_sym_name ( /*C++-demangle*/True, /*Z-demangle*/True, /*below-main-renaming*/True, - a, buf, + ep, a, buf, /*match_anywhere_in_fun*/True, /*show offset?*/True, /*text sym*/True, @@ -1901,14 +2103,14 @@ Bool VG_(get_fnname_w_offset) ( Addr a, const HChar** buf ) and don't show offsets. NOTE: See IMPORTANT COMMENT above about persistence and ownership in pub_tool_debuginfo.h */ -Bool VG_(get_fnname_if_entry) ( Addr a, const HChar** buf ) +Bool VG_(get_fnname_if_entry) ( DiEpoch ep, Addr a, const HChar** buf ) { const HChar *tmp; Bool res; res = get_sym_name ( /*C++-demangle*/True, /*Z-demangle*/True, /*below-main-renaming*/True, - a, &tmp, + ep, a, &tmp, /*match_anywhere_in_fun*/False, /*show offset?*/False, /*text sym*/True, @@ -1923,11 +2125,11 @@ Bool VG_(get_fnname_if_entry) ( Addr a, const HChar** buf ) offsets. NOTE: See IMPORTANT COMMENT above about persistence and ownership in pub_tool_debuginfo.h */ -Bool VG_(get_fnname_raw) ( Addr a, const HChar** buf ) +Bool VG_(get_fnname_raw) ( DiEpoch ep, Addr a, const HChar** buf ) { return get_sym_name ( /*C++-demangle*/False, /*Z-demangle*/False, /*below-main-renaming*/False, - a, buf, + ep, a, buf, /*match_anywhere_in_fun*/True, /*show offset?*/False, /*text sym*/True, @@ -1939,14 +2141,23 @@ Bool VG_(get_fnname_raw) ( Addr a, const HChar** buf ) don't show offsets. NOTE: See IMPORTANT COMMENT above about persistence and ownership in pub_tool_debuginfo.h */ -Bool VG_(get_fnname_no_cxx_demangle) ( Addr a, const HChar** buf, +Bool VG_(get_fnname_no_cxx_demangle) ( DiEpoch ep, Addr a, const HChar** buf, const InlIPCursor* iipc ) { + // All the callers of VG_(get_fnname_no_cxx_demangle) must build + // the iipc with the same ep as provided to VG_(get_fnname_no_cxx_demangle). + // So, if we have an iipc, iipc->di must be valid in the provided ep. + // Functionally, we could equally use iipc->di->first_epoch or ep, as + // all the inlined fn calls will be described by the same di. + if (iipc) { + vg_assert(is_DI_valid_for_epoch(iipc->di, ep)); + } + if (is_bottom(iipc)) { // At the bottom (towards main), we describe the fn at eip. return get_sym_name ( /*C++-demangle*/False, /*Z-demangle*/True, /*below-main-renaming*/True, - a, buf, + ep, a, buf, /*match_anywhere_in_fun*/True, /*show offset?*/False, /*text sym*/True, @@ -1965,13 +2176,13 @@ Bool VG_(get_fnname_no_cxx_demangle) ( Addr a, const HChar** buf, /* mips-linux only: find the offset of current address. This is needed for stack unwinding for MIPS. */ -Bool VG_(get_inst_offset_in_function)( Addr a, +Bool VG_(get_inst_offset_in_function)( DiEpoch ep, Addr a, /*OUT*/PtrdiffT* offset ) { const HChar *fnname; return get_sym_name ( /*C++-demangle*/False, /*Z-demangle*/False, /*below-main-renaming*/False, - a, &fnname, + ep, a, &fnname, /*match_anywhere_in_sym*/True, /*show offset?*/False, /*text sym*/True, @@ -2006,13 +2217,13 @@ Vg_FnNameKind VG_(get_fnname_kind) ( const HChar* name ) } } -Vg_FnNameKind VG_(get_fnname_kind_from_IP) ( Addr ip ) +Vg_FnNameKind VG_(get_fnname_kind_from_IP) ( DiEpoch ep, Addr ip ) { const HChar *buf; // We don't demangle, because it's faster not to, and the special names // we're looking for won't be mangled. - if (VG_(get_fnname_raw) ( ip, &buf )) { + if (VG_(get_fnname_raw) ( ep, ip, &buf )) { return VG_(get_fnname_kind)(buf); } else { @@ -2025,13 +2236,13 @@ Vg_FnNameKind VG_(get_fnname_kind_from_IP) ( Addr ip ) Also data_addr's offset from the symbol start is put into *offset. NOTE: See IMPORTANT COMMENT above about persistence and ownership in pub_tool_debuginfo.h */ -Bool VG_(get_datasym_and_offset)( Addr data_addr, +Bool VG_(get_datasym_and_offset)( DiEpoch ep, Addr data_addr, /*OUT*/const HChar** dname, /*OUT*/PtrdiffT* offset ) { return get_sym_name ( /*C++-demangle*/False, /*Z-demangle*/False, /*below-main-renaming*/False, - data_addr, dname, + ep, data_addr, dname, /*match_anywhere_in_sym*/True, /*show offset?*/False, /*text sym*/False, @@ -2044,7 +2255,7 @@ Bool VG_(get_datasym_and_offset)( Addr data_addr, (1) the DebugInfo it belongs to is not discarded (2) the segment containing the address is not merged with another segment */ -Bool VG_(get_objname) ( Addr a, const HChar** objname ) +Bool VG_(get_objname) ( DiEpoch ep, Addr a, const HChar** objname ) { DebugInfo* di; const NSegment *seg; @@ -2053,6 +2264,8 @@ Bool VG_(get_objname) ( Addr a, const HChar** objname ) /* Look in the debugInfo_list to find the name. In most cases we expect this to produce a result. */ for (di = debugInfo_list; di != NULL; di = di->next) { + if (!is_DI_valid_for_epoch(di, ep)) + continue; if (di->text_present && di->text_size > 0 && di->text_avma <= a @@ -2065,8 +2278,13 @@ Bool VG_(get_objname) ( Addr a, const HChar** objname ) the debugInfo_list, ask the address space manager whether it knows the name of the file associated with this mapping. This allows us to print the names of exe/dll files in the stack trace - when running programs under wine. */ - if ( (seg = VG_(am_find_nsegment)(a)) != NULL + when running programs under wine. + + Restrict this to the case where 'ep' is the current epoch, though, so + that we don't return information about this epoch when the caller was + enquiring about a different one. */ + if ( eq_DiEpoch(ep, VG_(current_DiEpoch)()) + && (seg = VG_(am_find_nsegment)(a)) != NULL && (filename = VG_(am_get_filename)(seg)) != NULL ) { *objname = filename; return True; @@ -2076,12 +2294,14 @@ Bool VG_(get_objname) ( Addr a, const HChar** objname ) /* Map a code address to its DebugInfo. Returns NULL if not found. Doesn't require debug info. */ -DebugInfo* VG_(find_DebugInfo) ( Addr a ) +DebugInfo* VG_(find_DebugInfo) ( DiEpoch ep, Addr a ) { static UWord n_search = 0; DebugInfo* di; n_search++; for (di = debugInfo_list; di != NULL; di = di->next) { + if (!is_DI_valid_for_epoch(di, ep)) + continue; if (di->text_present && di->text_size > 0 && di->text_avma <= a @@ -2097,13 +2317,13 @@ DebugInfo* VG_(find_DebugInfo) ( Addr a ) /* Map a code address to a filename. Returns True if successful. The returned string is persistent as long as the DebugInfo to which it belongs is not discarded. */ -Bool VG_(get_filename)( Addr a, const HChar** filename ) +Bool VG_(get_filename)( DiEpoch ep, Addr a, const HChar** filename ) { DebugInfo* si; Word locno; UInt fndn_ix; - search_all_loctabs ( a, &si, &locno ); + search_all_loctabs ( ep, a, &si, &locno ); if (si == NULL) return False; fndn_ix = ML_(fndn_ix) (si, locno); @@ -2112,11 +2332,11 @@ Bool VG_(get_filename)( Addr a, const HChar** filename ) } /* Map a code address to a line number. Returns True if successful. */ -Bool VG_(get_linenum)( Addr a, UInt* lineno ) +Bool VG_(get_linenum)( DiEpoch ep, Addr a, UInt* lineno ) { DebugInfo* si; Word locno; - search_all_loctabs ( a, &si, &locno ); + search_all_loctabs ( ep, a, &si, &locno ); if (si == NULL) return False; *lineno = si->loctab[locno].lineno; @@ -2127,7 +2347,7 @@ Bool VG_(get_linenum)( Addr a, UInt* lineno ) /* Map a code address to a filename/line number/dir name info. See prototype for detailed description of behaviour. */ -Bool VG_(get_filename_linenum) ( Addr a, +Bool VG_(get_filename_linenum) ( DiEpoch ep, Addr a, /*OUT*/const HChar** filename, /*OUT*/const HChar** dirname, /*OUT*/UInt* lineno ) @@ -2136,7 +2356,7 @@ Bool VG_(get_filename_linenum) ( Addr a, Word locno; UInt fndn_ix; - search_all_loctabs ( a, &si, &locno ); + search_all_loctabs ( ep, a, &si, &locno ); if (si == NULL) { if (dirname) { *dirname = ""; @@ -2165,7 +2385,8 @@ Bool VG_(get_filename_linenum) ( Addr a, Therefore specify "*" to search all the objects. On TOC-afflicted platforms, a symbol is deemed to be found only if it has a nonzero TOC pointer. */ -Bool VG_(lookup_symbol_SLOW)(const HChar* sopatt, const HChar* name, +Bool VG_(lookup_symbol_SLOW)(DiEpoch ep, + const HChar* sopatt, const HChar* name, SymAVMAs* avmas) { Bool require_pToc = False; @@ -2178,6 +2399,8 @@ Bool VG_(lookup_symbol_SLOW)(const HChar* sopatt, const HChar* name, for (si = debugInfo_list; si; si = si->next) { if (debug) VG_(printf)("lookup_symbol_SLOW: considering %s\n", si->soname); + if (!is_DI_valid_for_epoch(si, ep)) + continue; if (!VG_(string_match)(sopatt, si->soname)) { if (debug) VG_(printf)(" ... skip\n"); @@ -2260,7 +2483,7 @@ putStrEsc( SizeT n, HChar** buf, SizeT *bufsiz, const HChar* str ) return n; } -const HChar* VG_(describe_IP)(Addr eip, const InlIPCursor *iipc) +const HChar* VG_(describe_IP)(DiEpoch ep, Addr eip, const InlIPCursor *iipc) { static HChar *buf = NULL; static SizeT bufsiz = 0; @@ -2273,7 +2496,10 @@ const HChar* VG_(describe_IP)(Addr eip, const InlIPCursor *iipc) HChar ibuf[50]; // large enough SizeT n = 0; - vg_assert (!iipc || iipc->eip == eip); + // An InlIPCursor is associated with one specific DebugInfo. So if + // it exists, make sure that it is valid for the specified DiEpoch. + vg_assert (!iipc + || (is_DI_valid_for_epoch(iipc->di, ep) && iipc->eip == eip)); const HChar *buf_fn; const HChar *buf_obj; @@ -2288,8 +2514,8 @@ const HChar* VG_(describe_IP)(Addr eip, const InlIPCursor *iipc) if (is_bottom(iipc)) { // At the bottom (towards main), we describe the fn at eip. know_fnname = VG_(clo_sym_offsets) - ? VG_(get_fnname_w_offset) (eip, &buf_fn) - : VG_(get_fnname) (eip, &buf_fn); + ? VG_(get_fnname_w_offset) (ep, eip, &buf_fn) + : VG_(get_fnname) (ep, eip, &buf_fn); } else { const DiInlLoc *next_inl = iipc && iipc->next_inltab >= 0 ? & iipc->di->inltab[iipc->next_inltab] @@ -2307,15 +2533,15 @@ const HChar* VG_(describe_IP)(Addr eip, const InlIPCursor *iipc) // ??? Currently never showing an offset. } - know_objname = VG_(get_objname)(eip, &buf_obj); + know_objname = VG_(get_objname)(ep, eip, &buf_obj); if (is_top(iipc)) { // The source for the highest level is in the loctab entry. know_srcloc = VG_(get_filename_linenum)( - eip, - &buf_srcloc, + ep, eip, + &buf_srcloc, &buf_dirname, - &lineno + &lineno ); know_dirinfo = buf_dirname[0] != '\0'; } else { @@ -2465,6 +2691,20 @@ const HChar* VG_(describe_IP)(Addr eip, const InlIPCursor *iipc) /*--- ---*/ /*--------------------------------------------------------------*/ +/* Note that the CFI machinery pertains to unwinding the stack "right now". + There is no support for unwinding stack images obtained from some time in + the past. That means that: + + (1) We only deal with CFI from DebugInfos that are valid for the current + debuginfo epoch. Unlike in the rest of the file, there is no + epoch-awareness. + + (2) We assume that the CFI cache will be invalidated every time the the + epoch changes. This is done by ensuring (in the file above) that + every call to advance_current_DiEpoch has a call to + caches__invalidate alongside it. +*/ + /* Gather up all the constant pieces of info needed to evaluate a CfiExpr into one convenient struct. */ typedef @@ -2585,6 +2825,9 @@ UWord evalCfiExpr ( const XArray* exprs, Int ix, *cfsi_mP to the cfsi_m pointer in that DebugInfo's cfsi_m_pool. If not found, set *diP to (DebugInfo*)1 and *cfsi_mP to zero. + + Per comments at the top of this section, we only look for CFI in + DebugInfos that are valid for the current epoch. */ __attribute__((noinline)) static void find_DiCfSI ( /*OUT*/DebugInfo** diP, @@ -2600,10 +2843,15 @@ static void find_DiCfSI ( /*OUT*/DebugInfo** diP, if (0) VG_(printf)("search for %#lx\n", ip); + DiEpoch curr_epoch = VG_(current_DiEpoch)(); + for (di = debugInfo_list; di != NULL; di = di->next) { Word j; n_steps++; + if (!is_DI_valid_for_epoch(di, curr_epoch)) + continue; + /* Use the per-DebugInfo summary address ranges to skip inapplicable DebugInfos quickly. */ if (di->cfsi_used == 0) @@ -2611,6 +2859,11 @@ static void find_DiCfSI ( /*OUT*/DebugInfo** diP, if (ip < di->cfsi_minavma || ip > di->cfsi_maxavma) continue; + // This di must be active (because we have explicitly chosen not to + // allow unwinding stacks that pertain to some past epoch). It can't + // be archived or not-yet-active. + vg_assert(is_DebugInfo_active(di)); + /* It might be in this DebugInfo. Search it. */ j = ML_(search_one_cfitab)( di, ip ); vg_assert(j >= -1 && j < (Word)di->cfsi_used); @@ -3044,6 +3297,7 @@ Bool VG_(use_CF_info) ( /*MOD*/D3UnwindRegs* uregsHere, Bool VG_(use_FPO_info) ( /*MOD*/Addr* ipP, /*MOD*/Addr* spP, /*MOD*/Addr* fpP, + DiEpoch ep, Addr min_accessible, Addr max_accessible ) { @@ -3061,6 +3315,9 @@ Bool VG_(use_FPO_info) ( /*MOD*/Addr* ipP, for (di = debugInfo_list; di != NULL; di = di->next) { n_steps++; + if (!is_DI_valid_for_epoch(di, ep)) + continue; + /* Use the per-DebugInfo summary address ranges to skip inapplicable DebugInfos quickly. */ if (di->fpo == NULL) @@ -3564,6 +3821,7 @@ static void format_message ( /*MOD*/XArray* /* of HChar */ dn1, static Bool consider_vars_in_frame ( /*MOD*/XArray* /* of HChar */ dname1, /*MOD*/XArray* /* of HChar */ dname2, + DiEpoch ep, Addr data_addr, Addr ip, Addr sp, Addr fp, /* shown to user: */ @@ -3582,6 +3840,8 @@ Bool consider_vars_in_frame ( /*MOD*/XArray* /* of HChar */ dname1, /* first, find the DebugInfo that pertains to 'ip'. */ for (di = debugInfo_list; di; di = di->next) { n_steps++; + if (!is_DI_valid_for_epoch(di, ep)) + continue; /* text segment missing? unlikely, but handle it .. */ if (!di->text_present || di->text_size == 0) continue; @@ -3699,7 +3959,7 @@ Bool consider_vars_in_frame ( /*MOD*/XArray* /* of HChar */ dname1, Bool VG_(get_data_description)( /*MOD*/ XArray* /* of HChar */ dname1, /*MOD*/ XArray* /* of HChar */ dname2, - Addr data_addr + DiEpoch ep, Addr data_addr ) { # define N_FRAMES 8 @@ -3819,7 +4079,7 @@ Bool VG_(get_data_description)( vg_assert(n_frames >= 0 && n_frames <= N_FRAMES); for (j = 0; j < n_frames; j++) { if (consider_vars_in_frame( dname1, dname2, - data_addr, + ep, data_addr, ips[j], sps[j], fps[j], tid, j )) { zterm_XA( dname1 ); @@ -3846,7 +4106,7 @@ Bool VG_(get_data_description)( equivalent kludge. */ if (j > 0 /* this is a non-innermost frame */ && consider_vars_in_frame( dname1, dname2, - data_addr, + ep, data_addr, ips[j] + 1, sps[j], fps[j], tid, j )) { zterm_XA( dname1 ); diff --git a/coregrind/m_debuginfo/priv_storage.h b/coregrind/m_debuginfo/priv_storage.h index 061ecf2..6c471a0 100644 --- a/coregrind/m_debuginfo/priv_storage.h +++ b/coregrind/m_debuginfo/priv_storage.h @@ -588,6 +588,36 @@ struct _DebugInfo { structure is allocated. */ ULong handle; + /* The range of epochs for which this DebugInfo is valid. These also + divide the DebugInfo's lifetime into three parts: + + (1) Allocated: but with only .fsm holding useful info -- in + particular, not yet holding any debug info. + .first_epoch == DebugInfoEpoch_INVALID + .last_epoch == DebugInfoEpoch_INVALID + + (2) Active: containing debug info, and current. + .first_epoch != DebugInfoEpoch_INVALID + .last_epoch == DebugInfoEpoch_INVALID + + (3) Archived: containing debug info, but no longer current. + .first_epoch != DebugInfoEpoch_INVALID + .last_epoch != DebugInfoEpoch_INVALID + + State (2) corresponds to an object which is currently mapped. When + the object is unmapped, what happens depends on the setting of + --keep-debuginfo: + + * when =no, the DebugInfo is removed from debugInfo_list and + deleted. + + * when =yes, the DebugInfo is retained in debugInfo_list, but its + .last_epoch field is filled in, and current_epoch is advanced. This + effectively moves the DebugInfo into state (3). + */ + DiEpoch first_epoch; + DiEpoch last_epoch; + /* Used for debugging only - indicate what stuff to dump whilst reading stuff into the seginfo. Are computed as early in the lifetime of the DebugInfo as possible -- at the point when it is diff --git a/coregrind/m_errormgr.c b/coregrind/m_errormgr.c index c1f8006..38e6769 100644 --- a/coregrind/m_errormgr.c +++ b/coregrind/m_errormgr.c @@ -321,15 +321,15 @@ static Bool eq_Error ( VgRes res, const Error* e1, const Error* e2 ) */ #define ERRTXT_LEN 4096 -static void printSuppForIp_XML(UInt n, Addr ip, void* uu_opaque) +static void printSuppForIp_XML(UInt n, DiEpoch ep, Addr ip, void* uu_opaque) { const HChar *buf; - InlIPCursor* iipc = VG_(new_IIPC)(ip); + InlIPCursor* iipc = VG_(new_IIPC)(ep, ip); do { - if ( VG_(get_fnname_no_cxx_demangle) (ip, &buf, iipc) ) { + if ( VG_(get_fnname_no_cxx_demangle) (ep, ip, &buf, iipc) ) { VG_(printf_xml)(" <sframe> <fun>%pS</fun> </sframe>\n", buf); } else - if ( VG_(get_objname)(ip, &buf) ) { + if ( VG_(get_objname)(ep, ip, &buf) ) { VG_(printf_xml)(" <sframe> <obj>%pS</obj> </sframe>\n", buf); } else { VG_(printf_xml)(" <sframe> <obj>*</obj> </sframe>\n"); @@ -338,16 +338,16 @@ static void printSuppForIp_XML(UInt n, Addr ip, void* uu_opaque) VG_(delete_IIPC)(iipc); } -static void printSuppForIp_nonXML(UInt n, Addr ip, void* textV) +static void printSuppForIp_nonXML(UInt n, DiEpoch ep, Addr ip, void* textV) { const HChar *buf; XArray* /* of HChar */ text = (XArray*)textV; - InlIPCursor* iipc = VG_(new_IIPC)(ip); + InlIPCursor* iipc = VG_(new_IIPC)(ep, ip); do { - if ( VG_(get_fnname_no_cxx_demangle) (ip, &buf, iipc) ) { + if ( VG_(get_fnname_no_cxx_demangle) (ep, ip, &buf, iipc) ) { VG_(xaprintf)(text, " fun:%s\n", buf); } else - if ( VG_(get_objname)(ip, &buf) ) { + if ( VG_(get_objname)(ep, ip, &buf) ) { VG_(xaprintf)(text, " obj:%s\n", buf); } else { VG_(xaprintf)(text, " obj:*\n"); @@ -412,7 +412,7 @@ static void gen_suppression(const Error* err) vg_assert(n_ips > 0); vg_assert(n_ips <= VG_DEEPEST_BACKTRACE); VG_(apply_StackTrace)(printSuppForIp_nonXML, - text, + text, VG_(get_ExeContext_epoch)(ec), VG_(get_ExeContext_StackTrace)(ec), n_ips); @@ -441,7 +441,7 @@ static void gen_suppression(const Error* err) // Print stack trace elements VG_(apply_StackTrace)(printSuppForIp_XML, - NULL, + NULL, VG_(get_ExeContext_epoch)(ec), VG_(get_ExeContext_StackTrace)(ec), VG_(get_ExeContext_n_ips)(ec)); @@ -660,7 +660,7 @@ void construct_error ( Error* err, ThreadId tid, ErrorKind ekind, Addr a, err->count = 1; err->tid = tid; if (NULL == where) - err->where = VG_(record_ExeContext)( tid, 0 ); + err->where = VG_(record_ExeContext)( tid, 0 ); else err->where = where; @@ -1509,6 +1509,7 @@ static Bool supploc_IsQuery ( const void* supplocV ) allocations and the nr of debuginfo search. */ typedef struct { + DiEpoch epoch; // used to interpret .ips StackTrace ips; // stack trace we are lazily completing. UWord n_ips; // nr of elements in ips. @@ -1604,9 +1605,10 @@ static void clearIPtoFunOrObjCompleter ( const Supp *su, su->sname, filename, su->sname_lineno); - } else + } else { VG_(dmsg)("errormgr matching end no suppression matched:\n"); - VG_(pp_StackTrace) (ip2fo->ips, ip2fo->n_ips); + } + VG_(pp_StackTrace) (ip2fo->epoch, ip2fo->ips, ip2fo->n_ips); pp_ip2fo(ip2fo); } if (ip2fo->n_offsets_per_ip) VG_(free)(ip2fo->n_offsets_per_ip); @@ -1669,7 +1671,8 @@ static HChar* foComplete(IPtoFunOrObjCompleter* ip2fo, // up comparing "malloc" in the suppression against // "_vgrZU_libcZdsoZa_malloc" in the backtrace, and the // two of them need to be made to match. - if (!VG_(get_fnname_no_cxx_demangle)(ip2fo->ips[ixInput], + if (!VG_(get_fnname_no_cxx_demangle)(ip2fo->epoch, + ip2fo->ips[ixInput], &caller, NULL)) caller = "???"; @@ -1690,7 +1693,7 @@ static HChar* foComplete(IPtoFunOrObjCompleter* ip2fo, last_expand_pos_ips is the last offset in fun/obj where ips[pos_ips] has been expanded. *... [truncated message content] |
|
From: Julian S. <se...@so...> - 2018-01-11 17:25:08
|
https://sourceware.org/git/gitweb.cgi?p=valgrind.git;h=f1a49eeb427caf42e7af2da2b91198e55c6f33b2 commit f1a49eeb427caf42e7af2da2b91198e55c6f33b2 Author: Julian Seward <js...@ac...> Date: Thu Jan 11 18:20:27 2018 +0100 Bug 385408 - s390x: z13 vector "support" instructions not implemented. Patch from Vadim Barkov (vb...@gm...). (from bug 385408 comment 0): Valgrind currently lacks support for the z/Architecture vector "support" instructions introduced with z13. These are documented in the z/Architecture Principles of Operation, Eleventh Edition (March, 2015), chapter 21: "Vector Overview and Support Instructions". Diff: --- .gitignore | 1 + VEX/priv/guest_s390_defs.h | 43 +- VEX/priv/guest_s390_helpers.c | 127 +- VEX/priv/guest_s390_toIR.c | 2122 ++++++++++- VEX/priv/host_s390_defs.c | 783 +++- VEX/priv/host_s390_defs.h | 80 +- VEX/priv/host_s390_isel.c | 508 ++- VEX/priv/ir_defs.c | 17 + VEX/priv/main_main.c | 3 + VEX/priv/s390_disasm.c | 64 + VEX/priv/s390_disasm.h | 4 +- VEX/pub/libvex.h | 4 +- VEX/pub/libvex_emnote.h | 3 + VEX/pub/libvex_guest_s390x.h | 130 +- VEX/pub/libvex_ir.h | 10 +- VEX/pub/libvex_s390x_common.h | 3 +- auxprogs/s390-check-opcodes.pl | 136 +- coregrind/m_coredump/coredump-elf.c | 3 +- coregrind/m_gdbserver/valgrind-low-s390x.c | 34 +- coregrind/m_machine.c | 9 +- coregrind/m_sigframe/sigframe-s390x-linux.c | 64 +- docs/internals/s390-opcodes.csv | 432 ++- memcheck/mc_machine.c | 12 +- memcheck/mc_translate.c | 16 + memcheck/tests/vbit-test/irops.c | 5 + none/tests/s390x/Makefile.am | 3 +- none/tests/s390x/stfle.c | 6 +- none/tests/s390x/stfle.stdout.exp | 12 +- none/tests/s390x/stfle.vgtest | 2 +- none/tests/s390x/vector.c | 244 ++ none/tests/s390x/vector.h | 173 + none/tests/s390x/vector.stderr.exp | 2 + none/tests/s390x/vector.stdout.exp | 5355 +++++++++++++++++++++++++++ none/tests/s390x/vector.vgtest | 2 + tests/s390x_features.c | 57 +- 35 files changed, 10035 insertions(+), 434 deletions(-) diff --git a/.gitignore b/.gitignore index 1410eb1..bb2cf9d 100644 --- a/.gitignore +++ b/.gitignore @@ -1857,6 +1857,7 @@ /none/tests/s390x/rxsbg /none/tests/s390x/fixbr /none/tests/s390x/popcnt +/none/tests/s390x/vector # /none/tests/scripts/ /none/tests/scripts/*.dSYM diff --git a/VEX/priv/guest_s390_defs.h b/VEX/priv/guest_s390_defs.h index 8ba6d94..d3dede7 100644 --- a/VEX/priv/guest_s390_defs.h +++ b/VEX/priv/guest_s390_defs.h @@ -80,7 +80,8 @@ ULong s390x_dirtyhelper_STCKF(ULong *addr); ULong s390x_dirtyhelper_STCKE(ULong *addr); ULong s390x_dirtyhelper_STFLE(VexGuestS390XState *guest_state, ULong *addr); void s390x_dirtyhelper_CUxy(UChar *addr, ULong data, ULong num_bytes); - +ULong s390x_dirtyhelper_vec_binop(VexGuestS390XState *guest_state, ULong opcode, + ULong v1, ULong v2); ULong s390_do_cu12_cu14_helper1(UInt byte1, UInt etf3_and_m3_is_1); ULong s390_do_cu12_helper2(UInt byte1, UInt byte2, UInt byte3, UInt byte4, ULong stuff); @@ -254,6 +255,46 @@ UInt s390_calculate_cond(ULong mask, ULong op, ULong dep1, ULong dep2, /* Last target instruction for the EX helper */ extern ULong last_execute_target; +/*------------------------------------------------------------*/ +/*--- Vector helpers. ---*/ +/*------------------------------------------------------------*/ + +/* Vector operatons which can change condition code */ +enum { + S390_CC_VEC_INVALID = 0, + S390_CC_VEC_VPKS = 1, + S390_CC_VEC_VPKLS = 2, + S390_CC_VEC_LAST = 3 // supposed to be the last element in enum +} s390x_cc_vec_binop; + +/* Create an "object" which contain information about vector operation + and it's element size. Used for passing data to dirtyhelper with one argument. +*/ +#define s390x_cc_vec_opcode(op, elem_size) ( ((op) << 3) | ((elem_size) & 0x07)) + +/* Extract operation from opcode created with "s390x_cc_vec_opcode" macro */ +#define s390x_cc_vec_get_op(opcode) ((opcode) >> 3) + +/* Extract operation from opcode created with "s390x_cc_vec_opcode" macro */ +#define s390x_cc_vec_get_elem_size(opcode) ((opcode) & 0x07) + + +/* Macro definitions for opcodes that are not generally available. + + The values to be encoded in those fields must be integer values in + hexadecimal notation without a leading 0x. + E.g. VRX_VXBD(e7, 1, 0, 3, 0000, 0, 06) is equal to "vl %%v1, 0(%%r3)\n\t" +*/ +#define VRX_VXBD(op1, v1, x2, b2, d2, rxb, op2) \ + ".short 0x" #op1 #v1 #x2 "\n\t .int 0x" #b2 #d2 "0" #rxb #op2 "\n\t" +#define VRR_VVVMM(op1, v1, v2, v3, m5, m4, rxb, op2) \ + ".short 0x" #op1 #v1 #v2 "\n\t .int 0x" #v3 "0" #m5 "0" #m4 #rxb #op2 "\n\t" + +#define VL(v1, x2, b2, d2, rxb) VRX_VXBD(e7, v1, x2, b2, d2, rxb, 06) +#define VPKS(v1, v2, v3, m4, m5, rxb) VRR_VVVMM(e7, v1, v2, v3, m5, m4, rxb, 97) +#define VPKLS(v1, v2, v3, m4, m5, rxb) VRR_VVVMM(e7, v1, v2, v3, m5, m4, rxb, 95) + + /*---------------------------------------------------------------*/ /*--- end guest_s390_defs.h ---*/ /*---------------------------------------------------------------*/ diff --git a/VEX/priv/guest_s390_helpers.c b/VEX/priv/guest_s390_helpers.c index aacd833..77772fe 100644 --- a/VEX/priv/guest_s390_helpers.c +++ b/VEX/priv/guest_s390_helpers.c @@ -68,26 +68,48 @@ LibVEX_GuestS390X_initialise(VexGuestS390XState *state) state->guest_a15 = 0; /*------------------------------------------------------------*/ -/*--- Initialise fpr registers ---*/ +/*--- Initialise vr registers ---*/ /*------------------------------------------------------------*/ - state->guest_f0 = 0; - state->guest_f1 = 0; - state->guest_f2 = 0; - state->guest_f3 = 0; - state->guest_f4 = 0; - state->guest_f5 = 0; - state->guest_f6 = 0; - state->guest_f7 = 0; - state->guest_f8 = 0; - state->guest_f9 = 0; - state->guest_f10 = 0; - state->guest_f11 = 0; - state->guest_f12 = 0; - state->guest_f13 = 0; - state->guest_f14 = 0; - state->guest_f15 = 0; - +#define VRZERO(vr) \ + do { \ + vr.w64[0] = vr.w64[1] = 0ULL; \ + } while(0); + + VRZERO(state->guest_v0) + VRZERO(state->guest_v1) + VRZERO(state->guest_v2) + VRZERO(state->guest_v3) + VRZERO(state->guest_v4) + VRZERO(state->guest_v5) + VRZERO(state->guest_v6) + VRZERO(state->guest_v7) + VRZERO(state->guest_v8) + VRZERO(state->guest_v9) + VRZERO(state->guest_v10) + VRZERO(state->guest_v11) + VRZERO(state->guest_v12) + VRZERO(state->guest_v13) + VRZERO(state->guest_v14) + VRZERO(state->guest_v15) + VRZERO(state->guest_v16) + VRZERO(state->guest_v17) + VRZERO(state->guest_v18) + VRZERO(state->guest_v19) + VRZERO(state->guest_v20) + VRZERO(state->guest_v21) + VRZERO(state->guest_v22) + VRZERO(state->guest_v23) + VRZERO(state->guest_v24) + VRZERO(state->guest_v25) + VRZERO(state->guest_v26) + VRZERO(state->guest_v27) + VRZERO(state->guest_v28) + VRZERO(state->guest_v29) + VRZERO(state->guest_v30) + VRZERO(state->guest_v31) + +#undef VRZERO /*------------------------------------------------------------*/ /*--- Initialise gpr registers ---*/ /*------------------------------------------------------------*/ @@ -1187,6 +1209,23 @@ decode_bfp_rounding_mode(UInt irrm) psw >> 28; /* cc */ \ }) +/* This macro believes that arguments' addresses are in GPR1 and GPR2. + We use %%v16, %%v17 and %%v18 to avoid side effects in FPRs. +*/ +#define S390_CC_FOR_V128_BINOP(insn) \ +({ \ + /* VL(v1, x2, b2, d2, rxb) */ \ + __asm__ volatile ( \ + VL(1, 0, 1, 000, 8) \ + VL(2, 0, 2, 000, 8) \ + insn \ + "ipm %[psw]\n\t" \ + : [psw] "=d"(psw) \ + : "d"(arg1), "d"(arg2) \ + : "cc", "v16", "v17", "v18"); \ + psw >> 28; /* cc */ \ +}) + /* Convert an IRRoundingMode value to s390_dfp_round_t */ #if defined(VGA_s390x) static s390_dfp_round_t @@ -2434,6 +2473,58 @@ missed: return NULL; } +/*------------------------------------------------------------*/ +/*--- Dirty helper for vector instructions ---*/ +/*------------------------------------------------------------*/ + +#if defined(VGA_s390x) +ULong +s390x_dirtyhelper_vec_binop(VexGuestS390XState *guest_state, ULong opcode, + ULong v1, ULong v2) +{ + UInt psw; + UInt elem_size = s390x_cc_vec_get_elem_size(opcode); + UInt op = s390x_cc_vec_get_op(opcode); + /* S390_CC_FOR_V128_BINOP relies on exatly this GPRs numbers and names. */ + register ULong arg1 asm("1") = (ULong) &((&guest_state->guest_v0)[v1]); + register ULong arg2 asm("2") = (ULong) &((&guest_state->guest_v0)[v2]); + + switch(op) { + case S390_CC_VEC_VPKS: + /* VPKS(v1, v2, v3, m4, m5, rxb) */ + switch(elem_size) { + case 1: return S390_CC_FOR_V128_BINOP(VPKS(3, 1, 2, 1, 1, e)); + case 2: return S390_CC_FOR_V128_BINOP(VPKS(3, 1, 2, 2, 1, e)); + case 3: return S390_CC_FOR_V128_BINOP(VPKS(3, 1, 2, 3, 1, e)); + default: vassert(0); + } + + case S390_CC_VEC_VPKLS: + /* VPKLS(v1, v2, v3, m4, m5, rxb) */ + switch(elem_size) { + case 1: return S390_CC_FOR_V128_BINOP(VPKLS(3, 1, 2, 1, 1, e)); + case 2: return S390_CC_FOR_V128_BINOP(VPKLS(3, 1, 2, 2, 1, e)); + case 3: return S390_CC_FOR_V128_BINOP(VPKLS(3, 1, 2, 3, 1, e)); + default: vassert(0); + } + + default: + vex_printf("operation = %d\n", op); + vpanic("s390x_dirtyhelper_vec_binop: unknown operation"); + } + + return 0; +} + +#else + +ULong +s390x_dirtyhelper_vec_binop(VexGuestS390XState *guest_state, ULong opcode, + ULong v1, ULong v2) +{ return 0; } + +#endif + /*---------------------------------------------------------------*/ /*--- end guest_s390_helpers.c ---*/ /*---------------------------------------------------------------*/ diff --git a/VEX/priv/guest_s390_toIR.c b/VEX/priv/guest_s390_toIR.c index b36b85e..cca684a 100644 --- a/VEX/priv/guest_s390_toIR.c +++ b/VEX/priv/guest_s390_toIR.c @@ -919,27 +919,29 @@ get_ar_w0(UInt archreg) /*--- fpr registers ---*/ /*------------------------------------------------------------*/ -/* Return the guest state offset of a fpr register. */ +/* Return the guest state offset of a fpr register. + FPRs are maped to first doubleword of VRs. +*/ static UInt fpr_offset(UInt archreg) { static const UInt offset[16] = { - S390X_GUEST_OFFSET(guest_f0), - S390X_GUEST_OFFSET(guest_f1), - S390X_GUEST_OFFSET(guest_f2), - S390X_GUEST_OFFSET(guest_f3), - S390X_GUEST_OFFSET(guest_f4), - S390X_GUEST_OFFSET(guest_f5), - S390X_GUEST_OFFSET(guest_f6), - S390X_GUEST_OFFSET(guest_f7), - S390X_GUEST_OFFSET(guest_f8), - S390X_GUEST_OFFSET(guest_f9), - S390X_GUEST_OFFSET(guest_f10), - S390X_GUEST_OFFSET(guest_f11), - S390X_GUEST_OFFSET(guest_f12), - S390X_GUEST_OFFSET(guest_f13), - S390X_GUEST_OFFSET(guest_f14), - S390X_GUEST_OFFSET(guest_f15), + S390X_GUEST_OFFSET(guest_v0), + S390X_GUEST_OFFSET(guest_v1), + S390X_GUEST_OFFSET(guest_v2), + S390X_GUEST_OFFSET(guest_v3), + S390X_GUEST_OFFSET(guest_v4), + S390X_GUEST_OFFSET(guest_v5), + S390X_GUEST_OFFSET(guest_v6), + S390X_GUEST_OFFSET(guest_v7), + S390X_GUEST_OFFSET(guest_v8), + S390X_GUEST_OFFSET(guest_v9), + S390X_GUEST_OFFSET(guest_v10), + S390X_GUEST_OFFSET(guest_v11), + S390X_GUEST_OFFSET(guest_v12), + S390X_GUEST_OFFSET(guest_v13), + S390X_GUEST_OFFSET(guest_v14), + S390X_GUEST_OFFSET(guest_v15), }; vassert(archreg < 16); @@ -1512,6 +1514,396 @@ get_fpc_w0(void) /*------------------------------------------------------------*/ +/*--- vr registers ---*/ +/*------------------------------------------------------------*/ + +/* Return the guest state offset of a vr register. */ +static UInt +vr_offset(UInt archreg) +{ + static const UInt offset[32] = { + S390X_GUEST_OFFSET(guest_v0), + S390X_GUEST_OFFSET(guest_v1), + S390X_GUEST_OFFSET(guest_v2), + S390X_GUEST_OFFSET(guest_v3), + S390X_GUEST_OFFSET(guest_v4), + S390X_GUEST_OFFSET(guest_v5), + S390X_GUEST_OFFSET(guest_v6), + S390X_GUEST_OFFSET(guest_v7), + S390X_GUEST_OFFSET(guest_v8), + S390X_GUEST_OFFSET(guest_v9), + S390X_GUEST_OFFSET(guest_v10), + S390X_GUEST_OFFSET(guest_v11), + S390X_GUEST_OFFSET(guest_v12), + S390X_GUEST_OFFSET(guest_v13), + S390X_GUEST_OFFSET(guest_v14), + S390X_GUEST_OFFSET(guest_v15), + S390X_GUEST_OFFSET(guest_v16), + S390X_GUEST_OFFSET(guest_v17), + S390X_GUEST_OFFSET(guest_v18), + S390X_GUEST_OFFSET(guest_v19), + S390X_GUEST_OFFSET(guest_v20), + S390X_GUEST_OFFSET(guest_v21), + S390X_GUEST_OFFSET(guest_v22), + S390X_GUEST_OFFSET(guest_v23), + S390X_GUEST_OFFSET(guest_v24), + S390X_GUEST_OFFSET(guest_v25), + S390X_GUEST_OFFSET(guest_v26), + S390X_GUEST_OFFSET(guest_v27), + S390X_GUEST_OFFSET(guest_v28), + S390X_GUEST_OFFSET(guest_v29), + S390X_GUEST_OFFSET(guest_v30), + S390X_GUEST_OFFSET(guest_v31), + }; + + vassert(archreg < 32); + + return offset[archreg]; +} + +/* Return the guest state offset of quadword of a vr register. */ +static UInt +vr_qw_offset(UInt archreg) +{ + return vr_offset(archreg) + 0; +} + +/* Write quadword of a vr to the guest state. */ +static void +put_vr_qw(UInt archreg, IRExpr *expr) +{ + vassert(typeOfIRExpr(irsb->tyenv, expr) == Ity_V128); + + stmt(IRStmt_Put(vr_qw_offset(archreg), expr)); +} + +/* Read quadword of a vr register. */ +static IRExpr * +get_vr_qw(UInt archreg) +{ + return IRExpr_Get(vr_qw_offset(archreg), Ity_V128); +} + +/* Return the guest state offset of double word #0 of a gpr register. */ +static UInt +vr_dw0_offset(UInt archreg) +{ + return vr_offset(archreg) + 0; +} + +/* Read doubleword #0 of a vr register. */ +static IRExpr * +get_vr_dw0(UInt archreg) +{ + return IRExpr_Get(vr_dw0_offset(archreg), Ity_I64); +} + +/* Write double word #0 of a vr to the guest state. */ +static void +put_vr_dw0(UInt archreg, IRExpr *expr) +{ + vassert(typeOfIRExpr(irsb->tyenv, expr) == Ity_I64); + + stmt(IRStmt_Put(vr_dw0_offset(archreg), expr)); +} + +/* Return the guest state offset of double word #1 of a gpr register. */ +static UInt +vr_dw1_offset(UInt archreg) +{ + return vr_offset(archreg) + 8; +} + +/* Read doubleword #1 of a vr register. */ +static IRExpr * +get_vr_dw1(UInt archreg) +{ + return IRExpr_Get(vr_dw1_offset(archreg), Ity_I64); +} + +/* Write double word #0 of a vr to the guest state. */ +static void +put_vr_dw1(UInt archreg, IRExpr *expr) +{ + vassert(typeOfIRExpr(irsb->tyenv, expr) == Ity_I64); + + stmt(IRStmt_Put(vr_dw1_offset(archreg), expr)); +} + +/* Return the guest state offset of word #1 of a gpr register. */ +static UInt +vr_w1_offset(UInt archreg) +{ + return vr_offset(archreg) + 4; +} + +/* Return the guest state offset of word #3 of a gpr register. */ +static UInt +vr_w3_offset(UInt archreg) +{ + return vr_offset(archreg) + 12; +} + +/* Read word #1 of a vr register. */ +static IRExpr * +get_vr_w1(UInt archreg) +{ + return IRExpr_Get(vr_w1_offset(archreg), Ity_I32); +} + +/* Read word #3 of a vr register. */ +static IRExpr * +get_vr_w3(UInt archreg) +{ + return IRExpr_Get(vr_w3_offset(archreg), Ity_I32); +} + +/* Return the guest state offset of halfword #3 of a gpr register. */ +static UInt +vr_hw3_offset(UInt archreg) +{ + return vr_offset(archreg) + 6; +} + +/* Read halfword #3 of a vr register. */ +static IRExpr * +get_vr_hw3(UInt archreg) +{ + return IRExpr_Get(vr_hw3_offset(archreg), Ity_I16); +} + +/* Return the guest state offset of halfword #7 of a gpr register. */ +static UInt +vr_hw7_offset(UInt archreg) +{ + return vr_offset(archreg) + 14; +} + +/* Read halfword #7 of a vr register. */ +static IRExpr * +get_vr_hw7(UInt archreg) +{ + return IRExpr_Get(vr_hw7_offset(archreg), Ity_I16); +} + +/* Return the guest state offset of byte #7 of a vr register. */ +static UInt +vr_b7_offset(UInt archreg) +{ + return vr_offset(archreg) + 7; +} + +/* Read byte #7 of a vr register. */ +static IRExpr * +get_vr_b7(UInt archreg) +{ + return IRExpr_Get(vr_b7_offset(archreg), Ity_I8); +} + +/* Return the guest state offset of byte #15 of a vr register. */ +static UInt +vr_b15_offset(UInt archreg) +{ + return vr_offset(archreg) + 15; +} + +/* Read byte #15 of a vr register. */ +static IRExpr * +get_vr_b15(UInt archreg) +{ + return IRExpr_Get(vr_b15_offset(archreg), Ity_I8); +} + +/* Determine IRType by instruction's m3 field */ +static IRType +s390_vr_get_type(const UChar m) +{ + static const IRType results[] = {Ity_I8, Ity_I16, Ity_I32, Ity_I64, Ity_V128}; + if (m > 4) { + vex_printf("s390_vr_get_type: m=%x\n", m); + vpanic("s390_vr_get_type: reserved m value"); + } + + return results[m]; +} + +/* Return the guest state offset of element with type's size and given index + of a vr register. +*/ +static UInt +s390_vr_offset_by_index(UInt archreg,IRType type, UChar index) +{ + switch (type) { + case Ity_I8: + if(index > 15) { + goto invalidIndex; + } + return vr_offset(archreg) + sizeof(UChar) * index; + + case Ity_I16: + if(index > 7) { + goto invalidIndex; + } + return vr_offset(archreg) + sizeof(UShort) * index; + + case Ity_I32: + if(index > 3) { + goto invalidIndex; + } + return vr_offset(archreg) + sizeof(UInt) * index; + + case Ity_I64: + if(index > 1) { + goto invalidIndex; + } + return vr_offset(archreg) + sizeof(ULong) * index; + case Ity_V128: + if(index == 0) { + return vr_qw_offset(archreg); + } else { + goto invalidIndex; + } + default: + vpanic("s390_vr_offset_by_index: unknown type"); + } + + invalidIndex: + vex_printf("s390_vr_offset_by_index: index = %d ; type = ", index); + ppIRType(type); + vpanic("s390_vr_offset_by_index: invalid index for given type"); +} + +/* Write type sized element to indexed part of vr to the guest state. */ +static void +put_vr(UInt archreg, IRType type, UChar index, IRExpr *expr) +{ + UInt offset = s390_vr_offset_by_index(archreg, type, index); + vassert(typeOfIRExpr(irsb->tyenv, expr) == type); + + stmt(IRStmt_Put(offset, expr)); +} + +/* Read type sized part specified by index of a vr register. */ +static IRExpr * +get_vr(UInt archreg, IRType type, UChar index) +{ + UInt offset = s390_vr_offset_by_index(archreg, type, index); + return IRExpr_Get(offset, type); +} + +/* Calculates vr index according to instruction's rxb field + and position of vr in instruction. + Index of first argument must be 1 (not zero) */ +static UChar +s390_vr_getVRindex(UChar v,UChar argNumber, UChar rxb) +{ + vassert(argNumber > 0 && argNumber <= 4); + vassert(rxb < 16); + return v | (((rxb) << (++argNumber)) & 0b00010000); +} + +static void +s390_vr_fill(UChar v1, IRExpr *o2) +{ + IRType o2type = typeOfIRExpr(irsb->tyenv, o2); + switch (o2type) { + case Ity_I8: + put_vr_qw(v1, unop(Iop_Dup8x16, o2)); + break; + case Ity_I16: + put_vr_qw(v1, unop(Iop_Dup16x8, o2)); + break; + case Ity_I32: + put_vr_qw(v1, unop(Iop_Dup32x4, o2)); + break; + case Ity_I64: + put_vr_dw0(v1, o2); + put_vr_dw1(v1, o2); + break; + default: + ppIRType(o2type); + vpanic("s390_vr_fill: invalid IRType"); + } +} + +/* Returns Ity_I32 number of bytes till block boundary specified by m */ +static IRExpr* +s390_getCountToBlockBoundary(IRTemp op2addr, UChar m) +{ + IRTemp boundary = newTemp(Ity_I32); + IRTemp sixteen = newTemp(Ity_I32); + IRTemp divisionResult = newTemp(Ity_I64); + IRTemp mod_result = newTemp(Ity_I32); + IRTemp output = newTemp(Ity_I32); + + switch (m) { + case 0: assign(boundary, mkU32(64)); break; + case 1: assign(boundary, mkU32(128)); break; + case 2: assign(boundary, mkU32(256)); break; + case 3: assign(boundary, mkU32(512)); break; + case 4: assign(boundary, mkU32(1024)); break; + case 5: assign(boundary, mkU32(2048)); break; + case 6: assign(boundary, mkU32(4096)); break; + default: + vex_printf("m = %d\n", m); + vpanic("s390_getCountToBlockBoundary: invalid m"); + } + assign(sixteen, mkU32(16)); + assign(divisionResult, + binop(Iop_DivModU64to32, mkexpr(op2addr), mkexpr(boundary))); + assign(mod_result, + binop(Iop_Sub32,mkexpr(boundary), + unop(Iop_64HIto32, mkexpr(divisionResult)))); + + assign(output, + mkite(binop(Iop_CmpLE32U, mkexpr(sixteen), mkexpr(mod_result)), + mkexpr(sixteen), + mkexpr(mod_result) + )); + + return mkexpr(output); +} + +/* Helper macro for s390_vr_loadWithLength */ +#define s390_vr_loadWithLength_process(elem) \ + put_vr_qw(v1, triop(Iop_SetElem8x16,\ + get_vr_qw(v1), mkU8(elem),\ + mkite(binop(Iop_CmpLE32U, mkU32(elem), mkexpr(maxIndexToLoad)),\ + load(Ity_I8, binop(Iop_Add64, mkexpr(addr), mkU64(elem))),\ + mkU8(0x00)\ + )\ + )\ + ) + +/* Load bytes into v1. + maxIndex specifies max index to load and must be Ity_I32. + If maxIndex > 16, all 16 bytes are loaded. + All bytes after maxIndex are zeroed. */ +static void s390_vr_loadWithLength(UChar v1, IRTemp addr, IRExpr *maxIndex) +{ + IRTemp maxIndexToLoad = newTemp(Ity_I32); + + assign(maxIndexToLoad, maxIndex); + + s390_vr_loadWithLength_process(0); + s390_vr_loadWithLength_process(1); + s390_vr_loadWithLength_process(2); + s390_vr_loadWithLength_process(3); + s390_vr_loadWithLength_process(4); + s390_vr_loadWithLength_process(5); + s390_vr_loadWithLength_process(6); + s390_vr_loadWithLength_process(7); + s390_vr_loadWithLength_process(8); + s390_vr_loadWithLength_process(9); + s390_vr_loadWithLength_process(10); + s390_vr_loadWithLength_process(11); + s390_vr_loadWithLength_process(12); + s390_vr_loadWithLength_process(13); + s390_vr_loadWithLength_process(14); + s390_vr_loadWithLength_process(15); +} + +/*------------------------------------------------------------*/ /*--- Rounding modes ---*/ /*------------------------------------------------------------*/ @@ -2588,90 +2980,448 @@ s390_format_SIL_RDU(const HChar *(*irgen)(UShort i2, IRTemp op1addr), s390_disasm(ENC3(MNM, UDXB, UINT), mnm, d1, 0, b1, i2); } +static void +s390_format_VRX_VRRD(const HChar *(*irgen)(UChar v1, IRTemp op2addr), + UChar v1, UChar x2, UChar b2, UShort d2, UChar rxb) +{ + const HChar *mnm; + IRTemp op2addr = newTemp(Ity_I64); + if (! s390_host_has_vx) { + emulation_failure(EmFail_S390X_vx); + return; + } -/*------------------------------------------------------------*/ -/*--- Build IR for opcodes ---*/ -/*------------------------------------------------------------*/ - -static const HChar * -s390_irgen_AR(UChar r1, UChar r2) -{ - IRTemp op1 = newTemp(Ity_I32); - IRTemp op2 = newTemp(Ity_I32); - IRTemp result = newTemp(Ity_I32); + assign(op2addr, binop(Iop_Add64, binop(Iop_Add64, mkU64(d2), + b2 != 0 ? get_gpr_dw0(b2) : mkU64(0)), x2 != 0 ? get_gpr_dw0(x2) : + mkU64(0))); - assign(op1, get_gpr_w1(r1)); - assign(op2, get_gpr_w1(r2)); - assign(result, binop(Iop_Add32, mkexpr(op1), mkexpr(op2))); - s390_cc_thunk_putSS(S390_CC_OP_SIGNED_ADD_32, op1, op2); - put_gpr_w1(r1, mkexpr(result)); + v1 = s390_vr_getVRindex(v1, 1, rxb); + mnm = irgen(v1, op2addr); - return "ar"; + if (UNLIKELY(vex_traceflags & VEX_TRACE_FE)) + s390_disasm(ENC3(MNM, VR, UDXB), mnm, v1, d2, x2, b2); } -static const HChar * -s390_irgen_AGR(UChar r1, UChar r2) + +static void +s390_format_VRX_VRRDM(const HChar *(*irgen)(UChar v1, IRTemp op2addr, UChar m3), + UChar v1, UChar x2, UChar b2, UShort d2, UChar m3, UChar rxb) { - IRTemp op1 = newTemp(Ity_I64); - IRTemp op2 = newTemp(Ity_I64); - IRTemp result = newTemp(Ity_I64); + const HChar *mnm; + IRTemp op2addr = newTemp(Ity_I64); - assign(op1, get_gpr_dw0(r1)); - assign(op2, get_gpr_dw0(r2)); - assign(result, binop(Iop_Add64, mkexpr(op1), mkexpr(op2))); - s390_cc_thunk_putSS(S390_CC_OP_SIGNED_ADD_64, op1, op2); - put_gpr_dw0(r1, mkexpr(result)); + if (! s390_host_has_vx) { + emulation_failure(EmFail_S390X_vx); + return; + } - return "agr"; + assign(op2addr, binop(Iop_Add64, binop(Iop_Add64, mkU64(d2), + b2 != 0 ? get_gpr_dw0(b2) : mkU64(0)), x2 != 0 ? get_gpr_dw0(x2) : + mkU64(0))); + + v1 = s390_vr_getVRindex(v1, 1, rxb); + mnm = irgen(v1, op2addr, m3); + + if (UNLIKELY(vex_traceflags & VEX_TRACE_FE)) + s390_disasm(ENC3(MNM, VR, UDXB), mnm, v1, d2, x2, b2); } -static const HChar * -s390_irgen_AGFR(UChar r1, UChar r2) + +static void +s390_format_VRR_VV(const HChar *(*irgen)(UChar v1, UChar v2), + UChar v1, UChar v2, UChar rxb) { - IRTemp op1 = newTemp(Ity_I64); - IRTemp op2 = newTemp(Ity_I64); - IRTemp result = newTemp(Ity_I64); + const HChar *mnm; - assign(op1, get_gpr_dw0(r1)); - assign(op2, unop(Iop_32Sto64, get_gpr_w1(r2))); - assign(result, binop(Iop_Add64, mkexpr(op1), mkexpr(op2))); - s390_cc_thunk_putSS(S390_CC_OP_SIGNED_ADD_64, op1, op2); - put_gpr_dw0(r1, mkexpr(result)); + if (! s390_host_has_vx) { + emulation_failure(EmFail_S390X_vx); + return; + } - return "agfr"; + v1 = s390_vr_getVRindex(v1, 1, rxb); + v2 = s390_vr_getVRindex(v2, 2, rxb); + mnm = irgen(v1, v2); + + if (UNLIKELY(vex_traceflags & VEX_TRACE_FE)) + s390_disasm(ENC3(MNM, VR, VR), mnm, v1, v2); } -static const HChar * -s390_irgen_ARK(UChar r3, UChar r1, UChar r2) + +static void +s390_format_VRR_VVV(const HChar *(*irgen)(UChar v1, UChar v2, UChar v3), + UChar v1, UChar v2, UChar v3, UChar rxb) { - IRTemp op2 = newTemp(Ity_I32); - IRTemp op3 = newTemp(Ity_I32); - IRTemp result = newTemp(Ity_I32); + const HChar *mnm; - assign(op2, get_gpr_w1(r2)); - assign(op3, get_gpr_w1(r3)); - assign(result, binop(Iop_Add32, mkexpr(op2), mkexpr(op3))); - s390_cc_thunk_putSS(S390_CC_OP_SIGNED_ADD_32, op2, op3); - put_gpr_w1(r1, mkexpr(result)); + if (! s390_host_has_vx) { + emulation_failure(EmFail_S390X_vx); + return; + } - return "ark"; + v1 = s390_vr_getVRindex(v1, 1, rxb); + v2 = s390_vr_getVRindex(v2, 2, rxb); + v3 = s390_vr_getVRindex(v3, 3, rxb); + mnm = irgen(v1, v2, v3); + + if (UNLIKELY(vex_traceflags & VEX_TRACE_FE)) + s390_disasm(ENC4(MNM, VR, VR, VR), mnm, v1, v2, v3); } -static const HChar * -s390_irgen_AGRK(UChar r3, UChar r1, UChar r2) + +static void +s390_format_VRR_VVVM(const HChar *(*irgen)(UChar v1, UChar v2, UChar v3, UChar m4), + UChar v1, UChar v2, UChar v3, UChar m4, UChar rxb) { - IRTemp op2 = newTemp(Ity_I64); - IRTemp op3 = newTemp(Ity_I64); - IRTemp result = newTemp(Ity_I64); + const HChar *mnm; - assign(op2, get_gpr_dw0(r2)); - assign(op3, get_gpr_dw0(r3)); - assign(result, binop(Iop_Add64, mkexpr(op2), mkexpr(op3))); - s390_cc_thunk_putSS(S390_CC_OP_SIGNED_ADD_64, op2, op3); - put_gpr_dw0(r1, mkexpr(result)); + if (! s390_host_has_vx) { + emulation_failure(EmFail_S390X_vx); + return; + } - return "agrk"; + v1 = s390_vr_getVRindex(v1, 1, rxb); + v2 = s390_vr_getVRindex(v2, 2, rxb); + v3 = s390_vr_getVRindex(v3, 3, rxb); + mnm = irgen(v1, v2, v3, m4); + + if (UNLIKELY(vex_traceflags & VEX_TRACE_FE)) + s390_disasm(ENC5(MNM, VR, VR, VR, UINT), mnm, v1, v2, v3, m4); +} + + +static void +s390_format_VRR_VVVMM(const HChar *(*irgen)(UChar v1, UChar v2, UChar v3, UChar m4, UChar m5), + UChar v1, UChar v2, UChar v3, UChar m4, UChar m5, UChar rxb) +{ + const HChar *mnm; + + if (! s390_host_has_vx) { + emulation_failure(EmFail_S390X_vx); + return; + } + + v1 = s390_vr_getVRindex(v1, 1, rxb); + v2 = s390_vr_getVRindex(v2, 2, rxb); + v3 = s390_vr_getVRindex(v3, 3, rxb); + mnm = irgen(v1, v2, v3, m4, m5); + + if (UNLIKELY(vex_traceflags & VEX_TRACE_FE)) + s390_disasm(ENC6(MNM, VR, VR, VR, UINT, UINT), mnm, v1, v2, v3, m4, m5); +} + + +static void +s390_format_VRR_VVVV(const HChar *(*irgen)(UChar v1, UChar v2, UChar v3, UChar v4), + UChar v1, UChar v2, UChar v3, UChar v4, UChar rxb) +{ + const HChar *mnm; + + if (! s390_host_has_vx) { + emulation_failure(EmFail_S390X_vx); + return; + } + + v1 = s390_vr_getVRindex(v1, 1, rxb); + v2 = s390_vr_getVRindex(v2, 2, rxb); + v3 = s390_vr_getVRindex(v3, 3, rxb); + v4 = s390_vr_getVRindex(v4, 4, rxb); + mnm = irgen(v1, v2, v3, v4); + + if (UNLIKELY(vex_traceflags & VEX_TRACE_FE)) + s390_disasm(ENC5(MNM, VR, VR, VR, VR), mnm, v1, v2, v3, v4); +} + + +static void +s390_format_VRR_VRR(const HChar *(*irgen)(UChar v1, UChar r2, UChar r3), + UChar v1, UChar r2, UChar r3, UChar rxb) +{ + const HChar *mnm; + + if (! s390_host_has_vx) { + emulation_failure(EmFail_S390X_vx); + return; + } + + v1 = s390_vr_getVRindex(v1, 1, rxb); + mnm = irgen(v1, r2, r3); + + if (UNLIKELY(vex_traceflags & VEX_TRACE_FE)) + s390_disasm(ENC4(MNM, VR, GPR, GPR), mnm, v1, r2, r3); +} + + +static void +s390_format_VRR_VVM(const HChar *(*irgen)(UChar v1, UChar v2, UChar m3), + UChar v1, UChar v2, UChar m3, UChar rxb) +{ + const HChar *mnm; + + if (! s390_host_has_vx) { + emulation_failure(EmFail_S390X_vx); + return; + } + + v1 = s390_vr_getVRindex(v1, 1, rxb); + v2 = s390_vr_getVRindex(v2, 2, rxb); + mnm = irgen(v1, v2, m3); + + if (UNLIKELY(vex_traceflags & VEX_TRACE_FE)) + s390_disasm(ENC4(MNM, VR, VR, UINT), mnm, v1, v2, m3); +} + + +static void +s390_format_VRI_VIM(const HChar *(*irgen)(UChar v1, UShort i2, UChar m3), + UChar v1, UShort i2, UChar m3, UChar rxb) +{ + const HChar *mnm; + + if (! s390_host_has_vx) { + emulation_failure(EmFail_S390X_vx); + return; + } + + v1 = s390_vr_getVRindex(v1, 1, rxb); + mnm = irgen(v1, i2, m3); + + if (UNLIKELY(vex_traceflags & VEX_TRACE_FE)) + s390_disasm(ENC4(MNM, VR, UINT, UINT), mnm, v1, i2, m3); +} + + +static void +s390_format_VRI_VVIM(const HChar *(*irgen)(UChar v1, UChar v3, UShort i2, UChar m4), + UChar v1, UChar v3, UShort i2, UChar m4, UChar rxb) +{ + const HChar *mnm; + + if (! s390_host_has_vx) { + emulation_failure(EmFail_S390X_vx); + return; + } + + v1 = s390_vr_getVRindex(v1, 1, rxb); + v3 = s390_vr_getVRindex(v3, 2, rxb); + mnm = irgen(v1, v3, i2, m4); + + if (UNLIKELY(vex_traceflags & VEX_TRACE_FE)) + s390_disasm(ENC5(MNM, VR, VR, UINT, UINT), mnm, v1, v3, i2, m4); +} + + +static void +s390_format_VRS_RRDVM(const HChar *(*irgen)(UChar r1, IRTemp op2addr, UChar v3, + UChar m4), UChar r1, UChar b2, UShort d2, UChar v3, + UChar m4, UChar rxb) +{ + const HChar *mnm; + IRTemp op2addr = newTemp(Ity_I64); + + if (! s390_host_has_vx) { + emulation_failure(EmFail_S390X_vx); + return; + } + + assign(op2addr, binop(Iop_Add64, mkU64(d2), b2 != 0 ? get_gpr_dw0(b2) : + mkU64(0))); + + v3 = s390_vr_getVRindex(v3, 2, rxb); + mnm = irgen(r1, op2addr, v3, m4); + + if (UNLIKELY(vex_traceflags & VEX_TRACE_FE)) + s390_disasm(ENC5(MNM, GPR, UDXB, VR, UINT), mnm, r1, d2, 0, b2, v3, m4); +} + + +static void +s390_format_VRS_VRDV(const HChar *(*irgen)(UChar v1, IRTemp op2addr, UChar v3), + UChar v1, UChar b2, UShort d2, UChar v3, UChar rxb) +{ + const HChar *mnm; + IRTemp op2addr = newTemp(Ity_I64); + + if (! s390_host_has_vx) { + emulation_failure(EmFail_S390X_vx); + return; + } + + assign(op2addr, binop(Iop_Add64, mkU64(d2), b2 != 0 ? get_gpr_dw0(b2) : + mkU64(0))); + + v1 = s390_vr_getVRindex(v1, 1, rxb); + v3 = s390_vr_getVRindex(v3, 2, rxb); + mnm = irgen(v1, op2addr, v3); + + if (UNLIKELY(vex_traceflags & VEX_TRACE_FE)) + s390_disasm(ENC4(MNM, VR, UDXB, VR), mnm, v1, d2, 0, b2, v3); +} + + +static void +s390_format_VRS_VRRDM(const HChar *(*irgen)(UChar v1, IRTemp op2addr, UChar r3, + UChar m4), + UChar v1, UChar b2, UShort d2, UChar r3, UChar m4, UChar rxb) +{ + const HChar *mnm; + IRTemp op2addr = newTemp(Ity_I64); + + if (! s390_host_has_vx) { + emulation_failure(EmFail_S390X_vx); + return; + } + + assign(op2addr, binop(Iop_Add64, mkU64(d2), b2 != 0 ? get_gpr_dw0(b2) : + mkU64(0))); + + v1 = s390_vr_getVRindex(v1, 1, rxb); + mnm = irgen(v1, op2addr, r3, m4); + + if (UNLIKELY(vex_traceflags & VEX_TRACE_FE)) + s390_disasm(ENC5(MNM, VR, GPR, UDXB, UINT), mnm, v1, r3, d2, 0, b2, m4); +} + + +static void +s390_format_VRS_VRRD(const HChar *(*irgen)(UChar v1, IRTemp op2addr, UChar r3), + UChar v1, UChar b2, UShort d2, UChar r3, UChar rxb) +{ + const HChar *mnm; + IRTemp op2addr = newTemp(Ity_I64); + + if (! s390_host_has_vx) { + emulation_failure(EmFail_S390X_vx); + return; + } + + assign(op2addr, binop(Iop_Add64, mkU64(d2), b2 != 0 ? get_gpr_dw0(b2) : + mkU64(0))); + + v1 = s390_vr_getVRindex(v1, 1, rxb); + mnm = irgen(v1, op2addr, r3); + + if (UNLIKELY(vex_traceflags & VEX_TRACE_FE)) + s390_disasm(ENC4(MNM, VR, GPR, UDXB), mnm, v1, r3, d2, 0, b2); +} + + +static void +s390_format_VRV_VVRDMT(const HChar *(*irgen)(UChar v1, IRTemp op2addr, UChar m3), + UChar v1, UChar v2, UChar b2, UShort d2, UChar m3, UChar rxb, + IRType type) +{ + const HChar *mnm; + IRTemp op2addr = newTemp(Ity_I64); + + if (! s390_host_has_vx) { + emulation_failure(EmFail_S390X_vx); + return; + } + + v1 = s390_vr_getVRindex(v1, 1, rxb); + v2 = s390_vr_getVRindex(v2, 2, rxb); + + vassert(type == Ity_I32 || type == Ity_I64); + IRExpr *x2; + if(type == Ity_I32) { + x2 = unop(Iop_32Uto64, get_vr(v2, type, m3)); + } else { + x2 = get_vr(v2, type, m3); + } + + assign(op2addr, binop(Iop_Add64, binop(Iop_Add64, mkU64(d2), + b2 != 0 ? get_gpr_dw0(b2) : mkU64(0)), x2)); + + mnm = irgen(v1, op2addr, m3); + + if (UNLIKELY(vex_traceflags & VEX_TRACE_FE)) + s390_disasm(ENC4(MNM, VR, UDVB, UINT), mnm, v1, d2, v2, b2, m3); +} + + +/*------------------------------------------------------------*/ +/*--- Build IR for opcodes ---*/ +/*------------------------------------------------------------*/ + +static const HChar * +s390_irgen_AR(UChar r1, UChar r2) +{ + IRTemp op1 = newTemp(Ity_I32); + IRTemp op2 = newTemp(Ity_I32); + IRTemp result = newTemp(Ity_I32); + + assign(op1, get_gpr_w1(r1)); + assign(op2, get_gpr_w1(r2)); + assign(result, binop(Iop_Add32, mkexpr(op1), mkexpr(op2))); + s390_cc_thunk_putSS(S390_CC_OP_SIGNED_ADD_32, op1, op2); + put_gpr_w1(r1, mkexpr(result)); + + return "ar"; +} + +static const HChar * +s390_irgen_AGR(UChar r1, UChar r2) +{ + IRTemp op1 = newTemp(Ity_I64); + IRTemp op2 = newTemp(Ity_I64); + IRTemp result = newTemp(Ity_I64); + + assign(op1, get_gpr_dw0(r1)); + assign(op2, get_gpr_dw0(r2)); + assign(result, binop(Iop_Add64, mkexpr(op1), mkexpr(op2))); + s390_cc_thunk_putSS(S390_CC_OP_SIGNED_ADD_64, op1, op2); + put_gpr_dw0(r1, mkexpr(result)); + + return "agr"; +} + +static const HChar * +s390_irgen_AGFR(UChar r1, UChar r2) +{ + IRTemp op1 = newTemp(Ity_I64); + IRTemp op2 = newTemp(Ity_I64); + IRTemp result = newTemp(Ity_I64); + + assign(op1, get_gpr_dw0(r1)); + assign(op2, unop(Iop_32Sto64, get_gpr_w1(r2))); + assign(result, binop(Iop_Add64, mkexpr(op1), mkexpr(op2))); + s390_cc_thunk_putSS(S390_CC_OP_SIGNED_ADD_64, op1, op2); + put_gpr_dw0(r1, mkexpr(result)); + + return "agfr"; +} + +static const HChar * +s390_irgen_ARK(UChar r3, UChar r1, UChar r2) +{ + IRTemp op2 = newTemp(Ity_I32); + IRTemp op3 = newTemp(Ity_I32); + IRTemp result = newTemp(Ity_I32); + + assign(op2, get_gpr_w1(r2)); + assign(op3, get_gpr_w1(r3)); + assign(result, binop(Iop_Add32, mkexpr(op2), mkexpr(op3))); + s390_cc_thunk_putSS(S390_CC_OP_SIGNED_ADD_32, op2, op3); + put_gpr_w1(r1, mkexpr(result)); + + return "ark"; +} + +static const HChar * +s390_irgen_AGRK(UChar r3, UChar r1, UChar r2) +{ + IRTemp op2 = newTemp(Ity_I64); + IRTemp op3 = newTemp(Ity_I64); + IRTemp result = newTemp(Ity_I64); + + assign(op2, get_gpr_dw0(r2)); + assign(op3, get_gpr_dw0(r3)); + assign(result, binop(Iop_Add64, mkexpr(op2), mkexpr(op3))); + s390_cc_thunk_putSS(S390_CC_OP_SIGNED_ADD_64, op2, op3); + put_gpr_dw0(r1, mkexpr(result)); + + return "agrk"; } static const HChar * @@ -14136,33 +14886,846 @@ s390_irgen_ECAG(UChar r1, UChar r3 __attribute__((unused)), IRTemp op2addr) return "ecag"; } +static const HChar * +s390_irgen_VL(UChar v1, IRTemp op2addr) +{ + put_vr_qw(v1, load(Ity_V128, mkexpr(op2addr))); -/* New insns are added here. - If an insn is contingent on a facility being installed also - check whether the list of supported facilities in function - s390x_dirtyhelper_STFLE needs updating */ - -/*------------------------------------------------------------*/ -/*--- Build IR for special instructions ---*/ -/*------------------------------------------------------------*/ + return "vl"; +} -static void -s390_irgen_client_request(void) +static const HChar * +s390_irgen_VLR(UChar v1, UChar v2) { - if (0) - vex_printf("%%R3 = client_request ( %%R2 )\n"); + put_vr_qw(v1, get_vr_qw(v2)); - Addr64 next = guest_IA_curr_instr + S390_SPECIAL_OP_PREAMBLE_SIZE - + S390_SPECIAL_OP_SIZE; + return "vlr"; +} - dis_res->jk_StopHere = Ijk_ClientReq; - dis_res->whatNext = Dis_StopHere; +static const HChar * +s390_irgen_VST(UChar v1, IRTemp op2addr) +{ + store(mkexpr(op2addr), get_vr_qw(v1)); - put_IA(mkaddr_expr(next)); + return "vst"; } -static void -s390_irgen_guest_NRADDR(void) +static const HChar * +s390_irgen_VLREP(UChar v1, IRTemp op2addr, UChar m3) +{ + IRType o2type = s390_vr_get_type(m3); + IRExpr* o2 = load(o2type, mkexpr(op2addr)); + s390_vr_fill(v1, o2); + return "vlrep"; +} + +static const HChar * +s390_irgen_VLEB(UChar v1, IRTemp op2addr, UChar m3) +{ + IRExpr* o2 = load(Ity_I8, mkexpr(op2addr)); + put_vr(v1, Ity_I8, m3, o2); + + return "vleb"; +} + +static const HChar * +s390_irgen_VLEH(UChar v1, IRTemp op2addr, UChar m3) +{ + IRExpr* o2 = load(Ity_I16, mkexpr(op2addr)); + put_vr(v1, Ity_I16, m3, o2); + + return "vleh"; +} + +static const HChar * +s390_irgen_VLEF(UChar v1, IRTemp op2addr, UChar m3) +{ + IRExpr* o2 = load(Ity_I32, mkexpr(op2addr)); + put_vr(v1, Ity_I32, m3, o2); + + return "vlef"; +} + +static const HChar * +s390_irgen_VLEG(UChar v1, IRTemp op2addr, UChar m3) +{ + IRExpr* o2 = load(Ity_I64, mkexpr(op2addr)); + put_vr(v1, Ity_I64, m3, o2); + + return "vleg"; +} + +static const HChar * +s390_irgen_VLEIB(UChar v1, UShort i2, UChar m3) +{ + IRExpr* o2 = unop(Iop_16to8, mkU16(i2)); + put_vr(v1, Ity_I8, m3, o2); + + return "vleib"; +} + +static const HChar * +s390_irgen_VLEIH(UChar v1, UShort i2, UChar m3) +{ + IRExpr* o2 = mkU16(i2); + put_vr(v1, Ity_I16, m3, o2); + + return "vleih"; +} + +static const HChar * +s390_irgen_VLEIF(UChar v1, UShort i2, UChar m3) +{ + IRExpr* o2 = unop(Iop_16Sto32, mkU16(i2)); + put_vr(v1, Ity_I32, m3, o2); + + return "vleif"; +} + +static const HChar * +s390_irgen_VLEIG(UChar v1, UShort i2, UChar m3) +{ + IRExpr* o2 = unop(Iop_16Sto64, mkU16(i2)); + put_vr(v1, Ity_I64, m3, o2); + + return "vleig"; +} + +static const HChar * +s390_irgen_VLGV(UChar r1, IRTemp op2addr, UChar v3, UChar m4) +{ + IRType o2type = s390_vr_get_type(m4); + IRExpr* index = unop(Iop_64to8, binop(Iop_And64, mkexpr(op2addr), mkU64(0xf))); + IRExpr* o2; + IRExpr* result; + switch (o2type) { + case Ity_I8: + o2 = binop(Iop_GetElem8x16, get_vr_qw(v3), index); + result = unop(Iop_8Uto64, o2); + break; + case Ity_I16: + o2 = binop(Iop_GetElem16x8, get_vr_qw(v3), index); + result = unop(Iop_16Uto64, o2); + break; + case Ity_I32: + o2 = binop(Iop_GetElem32x4, get_vr_qw(v3), index); + result = unop(Iop_32Uto64, o2); + break; + case Ity_I64: + result = binop(Iop_GetElem64x2, get_vr_qw(v3), index); + break; + default: + ppIRType(o2type); + vpanic("s390_irgen_VLGV: unknown o2type"); + } + + put_gpr_dw0(r1, result); + return "vlgv"; +} + +static const HChar * +s390_irgen_VGBM(UChar v1, UShort i2, UChar m3 __attribute__((unused))) +{ + put_vr_qw(v1, IRExpr_Const(IRConst_V128(i2))); + + return "vgbm"; +} + +static const HChar * +s390_irgen_VGM(UChar v1, UShort i2, UChar m3) +{ + UChar from = (i2 & 0xff00) >> 8; + UChar to = (i2 & 0x00ff); + ULong value = 0UL; + IRType type = s390_vr_get_type(m3); + vassert(from <= to); + + UChar maxIndex = 0; + switch (type) { + case Ity_I8: + maxIndex = 7; + break; + case Ity_I16: + maxIndex = 15; + break; + case Ity_I32: + maxIndex = 31; + break; + case Ity_I64: + maxIndex = 63; + break; + default: + vpanic("s390_irgen_VGM: unknown type"); + } + + for(UChar index = from; index <= to; index++) { + value |= (1ULL << (maxIndex - index)); + } + + IRExpr *fillValue; + switch (type) { + case Ity_I8: + fillValue = mkU8(value); + break; + case Ity_I16: + fillValue = mkU16(value); + break; + case Ity_I32: + fillValue = mkU32(value); + break; + case Ity_I64: + fillValue = mkU64(value); + break; + default: + vpanic("s390_irgen_VGM: unknown type"); + } + + s390_vr_fill(v1, fillValue); + return "vgm"; +} + +static const HChar * +s390_irgen_VLLEZ(UChar v1, IRTemp op2addr, UChar m3) +{ + IRType type = s390_vr_get_type(m3); + IRExpr* op2 = load(type, mkexpr(op2addr)); + IRExpr* op2as64bit; + switch (type) { + case Ity_I8: + op2as64bit = unop(Iop_8Uto64, op2); + break; + case Ity_I16: + op2as64bit = unop(Iop_16Uto64, op2); + break; + case Ity_I32: + op2as64bit = unop(Iop_32Uto64, op2); + break; + case Ity_I64: + op2as64bit = op2; + break; + default: + vpanic("s390_irgen_VLLEZ: unknown type"); + } + + put_vr_dw0(v1, op2as64bit); + put_vr_dw1(v1, mkU64(0)); + return "vllez"; +} + +static const HChar * +s390_irgen_VGEF(UChar v1, IRTemp op2addr, UChar m3) +{ + put_vr(v1, Ity_I32, m3, load(Ity_I32, mkexpr(op2addr))); + return "vgef"; +} + +static const HChar * +s390_irgen_VGEG(UChar v1, IRTemp op2addr, UChar m3) +{ + put_vr(v1, Ity_I64, m3, load(Ity_I64, mkexpr(op2addr))); + return "vgeg"; +} + +static const HChar * +s390_irgen_VLM(UChar v1, IRTemp op2addr, UChar v3) +{ + IRExpr* current = mkexpr(op2addr); + vassert(v3 >= v1); + vassert(v3 - v1 <= 16); + + for(UChar vr = v1; vr <= v3; vr++) { + IRExpr* next = binop(Iop_Add64, current, mkU64(16)); + put_vr_qw(vr, load(Ity_V128, current)); + current = next; + } + + return "vlm"; +} + +static const HChar * +s390_irgen_VLVGP(UChar v1, UChar r2, UChar r3) +{ + put_vr_dw0(v1, get_gpr_dw0(r2)); + put_vr_dw1(v1, get_gpr_dw0(r3)); + + return "vlvgp"; +} + +static const HChar * +s390_irgen_VLVG(UChar v1, IRTemp op2addr, UChar r3, UChar m4) +{ + IRType type = s390_vr_get_type(m4); + IRExpr* index = unop(Iop_64to8, mkexpr(op2addr)); + IRExpr* vr = get_vr_qw(v1); + IRExpr* operand; + switch (type) { + case Ity_I8: + operand = unop(Iop_64to8, get_gpr_dw0(r3)); + put_vr_qw(v1, triop(Iop_SetElem8x16, vr, index, operand)); + break; + case Ity_I16: + operand = unop(Iop_64to16, get_gpr_dw0(r3)); + put_vr_qw(v1, triop(Iop_SetElem16x8, vr, index, operand)); + break; + case Ity_I32: + operand = unop(Iop_64to32, get_gpr_dw0(r3)); + put_vr_qw(v1, triop(Iop_SetElem32x4, vr, index, operand)); + break; + case Ity_I64: + operand = get_gpr_dw0(r3); + put_vr_qw(v1, triop(Iop_SetElem64x2, vr, index, operand)); + break; + default: + vpanic("s390_irgen_VLVG: unknown type"); + } + + return "vlvg"; +} + +static const HChar * +s390_irgen_VMRH(UChar v1, UChar v2, UChar v3, UChar m4) +{ + IRType type = s390_vr_get_type(m4); + switch (type) { + case Ity_I8: + put_vr_qw(v1, binop(Iop_InterleaveLO8x16, get_vr_qw(v2), get_vr_qw(v3))); + break; + + case Ity_I16: + put_vr_qw(v1, binop(Iop_InterleaveLO16x8, get_vr_qw(v2), get_vr_qw(v3))); + break; + + case Ity_I32: + put_vr_qw(v1, binop(Iop_InterleaveLO32x4, get_vr_qw(v2), get_vr_qw(v3))); + break; + + case Ity_I64: + put_vr_qw(v1, binop(Iop_InterleaveLO64x2, get_vr_qw(v2), get_vr_qw(v3))); + break; + + default: + ppIRType(type); + vpanic("s390_irgen_VMRH: unknown type"); + } + + return "vmrh"; +} + +static const HChar * +s390_irgen_VMRL(UChar v1, UChar v2, UChar v3, UChar m4) +{ + IRType type = s390_vr_get_type(m4); + switch (type) { + case Ity_I8: + put_vr_qw(v1, binop(Iop_InterleaveHI8x16, get_vr_qw(v2), get_vr_qw(v3))); + break; + + case Ity_I16: + put_vr_qw(v1, binop(Iop_InterleaveHI16x8, get_vr_qw(v2), get_vr_qw(v3))); + break; + + case Ity_I32: + put_vr_qw(v1, binop(Iop_InterleaveHI32x4, get_vr_qw(v2), get_vr_qw(v3))); + break; + + case Ity_I64: + put_vr_qw(v1, binop(Iop_InterleaveHI64x2, get_vr_qw(v2), get_vr_qw(v3))); + break; + + default: + ppIRType(type); + vpanic("s390_irgen_VMRL: unknown type"); + } + + return "vmrl"; +} + +static const HChar * +s390_irgen_VPK(UChar v1, UChar v2, UChar v3, UChar m4) +{ + IRType type = s390_vr_get_type(m4); + IRExpr* result = NULL; + switch(type) { + case Ity_I16: + result = binop(Iop_NarrowBin16to8x16, get_vr_qw(v2), get_vr_qw(v3)); + break; + case Ity_I32: + result = binop(Iop_NarrowBin32to16x8, get_vr_qw(v2), get_vr_qw(v3)); + break; + case Ity_I64: + result = binop(Iop_NarrowBin64to32x4, get_vr_qw(v2), get_vr_qw(v3)); + break; + default: + ppIRType(type); + vpanic("s390_irgen_VPK: unknown type"); + } + + put_vr_qw(v1, result); + + return "vpk"; +} + +static const HChar * +s390_irgen_VPERM(UChar v1, UChar v2, UChar v3, UChar v4) +{ + put_vr_qw(v1, triop(Iop_Perm8x16x2, + get_vr_qw(v2), get_vr_qw(v3), get_vr_qw(v4))); + + return "vperm"; +} + +static const HChar * +s390_irgen_VSCEF(UChar v1, IRTemp op2addr, UChar m3) +{ + store(mkexpr(op2addr), get_vr(v1, Ity_I32, m3)); + return "vscef"; +} + +static const HChar * +s390_irgen_VSCEG(UChar v1, IRTemp op2addr, UChar m3) +{ + store(mkexpr(op2addr), get_vr(v1, Ity_I64, m3)); + return "vsceg"; +} + +static const HChar * +s390_irgen_VPDI(UChar v1, UChar v2, UChar v3, UChar m4) +{ + /* Theese bits are reserved by specification */ + vassert((m4 & 2) == 0); + vassert((m4 & 8) == 0); + + if((m4 & 4) != 0) + put_vr_dw0(v1, get_vr_dw1(v2)); + else + put_vr_dw0(v1, get_vr_dw0(v2)); + + if((m4 & 1) != 0) + put_vr_dw1(v1, get_vr_dw1(v3)); + else + put_vr_dw1(v1, get_vr_dw0(v3)); + + return "vpdi"; +} + +static const HChar * +s390_irgen_VSEG(UChar v1, UChar v2, UChar m3) +{ + IRType type = s390_vr_get_type(m3); + switch(type) { + case Ity_I8: + put_vr_dw0(v1, unop(Iop_8Sto64, get_vr_b7(v2))); + put_vr_dw1(v1, unop(Iop_8Sto64, get_vr_b15(v2))); + break; + case Ity_I16: + put_vr_dw0(v1, unop(Iop_16Sto64, get_vr_hw3(v2))); + put_vr_dw1(v1, unop(Iop_16Sto64, get_vr_hw7(v2))); + break; + case Ity_I32: + put_vr_dw0(v1, unop(Iop_32Sto64, get_vr_w1(v2))); + put_vr_dw1(v1, unop(Iop_32Sto64, get_vr_w3(v2))); + break; + default: + ppIRType(type); + vpanic("s390_irgen_VSEG: unknown type"); + } + + return "vseg"; +} + +static const HChar * +s390_irgen_VSTEB(UChar v1, IRTemp op2addr, UChar m3) +{ + store(mkexpr(op2addr), get_vr(v1, Ity_I8, m3)); + + return "vsteb"; +} + +static const HChar * +s390_irgen_VSTEH(UChar v1, IRTemp op2addr, UChar m3) +{ + store(mkexpr(op2addr), get_vr(v1, Ity_I16, m3)); + + return "vsteh"; +} + +static const HChar * +s390_irgen_VSTEF(UChar v1, IRTemp op2addr, UChar m3) +{ + store(mkexpr(op2addr), get_vr(v1, Ity_I32, m3)); + + return "vstef"; +} + +static const HChar * +s390_irgen_VSTEG(UChar v1, IRTemp op2addr, UChar m3) +{ + store(mkexpr(op2addr), get_vr(v1, Ity_I64, m3)); + + return "vsteg"; +} + +static const HChar * +s390_irgen_VSTM(UChar v1, IRTemp op2addr, UChar v3) +{ + IRExpr* current = mkexpr(op2addr); + vassert(v3 >= v1); + vassert(v3 - v1 <= 16); + + for(UChar vr = v1; vr <= v3; vr++) { + IRExpr* next = binop(Iop_Add64, current, mkU64(16)); + store(current, get_vr_qw(vr)); + current = next; + } + + return "vstm"; +} + +static const HChar * +s390_irgen_VUPH(UChar v1, UChar v2, UChar m3) +{ + IRType type = s390_vr_get_type(m3); + switch (type) { + case Ity_I8: + put_vr_qw(v1, unop(Iop_Widen8Sto16x8, get_vr_dw0(v2))); + break; + case Ity_I16: + put_vr_qw(v1, unop(Iop_Widen16Sto32x4, get_vr_dw0(v2))); + break; + case Ity_I32: + put_vr_qw(v1, unop(Iop_Widen32Sto64x2, get_vr_dw0(v2))); + break; + default: + ppIRType(type); + vpanic("s390_irgen_VUPH: unknown type"); + } + + return "vuph"; +} + +static const HChar * +s390_irgen_VUPLH(UChar v1, UChar v2, UChar m3) +{ + IRType type = s390_vr_get_type(m3); + switch (type) { + case Ity_I8: + put_vr_qw(v1, unop(Iop_Widen8Uto16x8, get_vr_dw0(v2))); + break; + case Ity_I16: + put_vr_qw(v1, unop(Iop_Widen16Uto32x4, get_vr_dw0(v2))); + break; + case Ity_I32: + put_vr_qw(v1, unop(Iop_Widen32Uto64x2, get_vr_dw0(v2))); + break; + default: + ppIRType(type); + vpanic("s390_irgen_VUPLH: unknown type"); + } + + return "vuplh"; +} + +static const HChar * +s390_irgen_VUPL(UChar v1, UChar v2, UChar m3) +{ + IRType type = s390_vr_get_type(m3); + switch (type) { + case Ity_I8: + put_vr_qw(v1, unop(Iop_Widen8Sto16x8, get_vr_dw1(v2))); + break; + case Ity_I16: + put_vr_qw(v1, unop(Iop_Widen16Sto32x4, get_vr_dw1(v2))); + break; + case Ity_I32: + put_vr_qw(v1, unop(Iop_Widen32Sto64x2, get_vr_dw1(v2))); + break; + default: + ppIRType(type); + vpanic("s390_irgen_VUPL: unknown type"); + } + + return "vupl"; +} + +static const HChar * +s390_irgen_VUPLL(UChar v1, UChar v2, UChar m3) +{ + IRType type = s390_vr_get_type(m3); + switch (type) { + case Ity_I8: + put_vr_qw(v1, unop(Iop_Widen8Uto16x8, get_vr_dw1(v2))); + break; + case Ity_I16: + put_vr_qw(v1, unop(Iop_Widen16Uto32x4, get_vr_dw1(v2))); + break; + case Ity_I32: + put_vr_qw(v1, unop(Iop_Widen32Uto64x2, get_vr_dw1(v2))); + break; + default: + ppIRType(type); + vpanic("s390_irgen_VUPLL: unknown type"); + } + + return "vupll"; +} + +static const HChar * +s390_irgen_VREP(UChar v1, UChar v3, UShort i2, UChar m4) +{ + IRType type = s390_vr_get_type(m4); + IRExpr* arg = get_vr(v3, type, i2); + s390_vr_fill(v1, arg); + + return "vrep"; +} + +static const HChar * +s390_irgen_VREPI(UChar v1, UShort i2, UChar m3) +{ + IRType type = s390_vr_get_type(m3); + IRExpr *value; + switch (type) { + case Ity_I8: + value = mkU8((UChar)i2); + break; + case Ity_I16: + value = mkU16(i2); + break; + case Ity_I32: + value = unop(Iop_16Sto32, mkU16(i2)); + break; + case Ity_I64: + value = unop(Iop_16Sto64, mkU16(i2)); + break; + default: + ppIRType(type); + vpanic("s390_irgen_VREPI: unknown type"); + } + s390_vr_fill(v1, value); + + return "vrepi"; +} + +static const HChar * +s390_irgen_VPKS(UChar v1, UChar v2, UChar v3, UChar m4, UChar m5) +{ + IRType type = s390_vr_get_type(m4); + IRExpr* result = NULL; + + switch(type) { + case Ity_I16: + result = binop(Iop_QNarrowBin16Sto8Sx16, get_vr_qw(v2), get_vr_qw(v3)); + break; + case Ity_I32: + result = binop(Iop_QNarrowBin32Sto16Sx8, get_vr_qw(v2), get_vr_qw(v3)); + break; + case Ity_I64: + result = binop(Iop_QNarrowBin64Sto32Sx4, get_vr_qw(v2), get_vr_qw(v3)); + break; + default: + ppIRType(type); + vpanic("s390_irgen_VPKS: unknown type"); + } + + if((m5 & 0x1) != 0) { + IRDirty* d; + IRTemp cc = newTemp(Ity_I64); + ULong opcode = s390x_cc_vec_opcode(S390_CC_VEC_VPKS, m4); + d = unsafeIRDirty_1_N(cc, 0, "s390x_dirtyhelper_vec_binop", + &s390x_dirtyhelper_vec_binop, + mkIRExprVec_4(IRExpr_GSPTR(), mkU64(opcode), + mkU64(v2), mkU64(v3))); + d->nFxState = 2; + vex_bzero(&d->fxState, sizeof(d->fxState)); + d->fxState[0].fx = Ifx_Read; + d->fxState[0].offset = S390X_GUEST_OFFSET(guest_v0) + v2 * sizeof(V128); + d->fxState[0].size = sizeof(V128); + d->fxState[1].fx = Ifx_Read; + d->fxState[1].offset = S390X_GUEST_OFFSET(guest_v0) + v3 * sizeof(V128); + d->fxState[1].size = sizeof(V128); + + stmt(IRStmt_Dirty(d)); + s390_cc_thunk_fill(mkU64(S390_CC_OP_SET), + mkexpr(cc), mkU64(0), mkU64(0)); + } + + put_vr_qw(v1, result); + return "vpks"; +} + +static const HChar * +s390_irgen_VPKLS(UChar v1, UChar v2, UChar v3, UChar m4, UChar m5) +{ + IRType type = s390_vr_get_type(m4); + IRExpr* result = NULL; + switch(type) { + case Ity_I16: + result = binop(Iop_QNarrowBin16Uto8Ux16, get_vr_qw(v2), get_vr_qw(v3)); + break; + case Ity_I32: + result = binop(Iop_QNarrowBin32Uto16Ux8, get_vr_qw(v2), get_vr_qw(v3)); + break; + case Ity_I64: + result = binop(Iop_QNarrowBin64Uto32Ux4, get_vr_qw(v2), get_vr_qw(v3)); + break; + default: + ppIRType(type); + vpanic("s390_irgen_VPKLS: unknown type"); + } + + if((m5 & 0x1) != 0) { + IRDirty* d; + IRTemp cc = newTemp(Ity_I64); + ULong opcode = s390x_cc_vec_opcode(S390_CC_VEC_VPKLS, m4); + d = unsafeIRDirty_1_N(cc, 0, "s390x_dirtyhelper_vec_binop", + &s390x_dirtyhelper_vec_binop, + mkIRExprVec_4(IRExpr_GSPTR(), mkU64(opcode), + mkU64(v2), mkU64(v3))); + d->nFxState = 2; + vex_bzero(&d->fxState, sizeof(d->fxState)); + d->fxState[0].fx = Ifx_Read; + d->fxState[0].offset = S390X_GUEST_OFFSET(guest_v0) + v2 * sizeof(V128); + d->fxState[0].size = sizeof(V128); + d->fxState[1].fx = Ifx_Read; + d->fxState[1].offset = S390X_GUEST_OFFSET(guest_v0) + v3 * sizeof(V128); + d->fxState[1].size = sizeof(V128); + + stmt(IRStmt_Dirty(d)); + s390_cc_thunk_fill(mkU64(S390_CC_OP_SET), + mkexpr(cc), mkU64(0), mkU64(0)); + } + + put_vr_qw(v1, result); + return "vpkls"; +} + +static const HChar * +s390_irgen_VSEL(UChar v1, UChar v2, UChar v3, UChar v4) +{ + IRExpr* vA = get_vr_qw(v3); + IRExpr* vB = get_vr_qw(v2); + IRExpr* vC = get_vr_qw(v4); + + /* result = (vA & ~vC) | (vB & vC) */ + put_vr_qw(v1, + binop(Iop_OrV128, + binop(Iop_AndV128, vA, unop(Iop_NotV128, vC)), + binop(Iop_AndV128, vB, vC) + ) + ); + return "vsel"; +} + +static const HChar * +s390_irgen_VLBB(UChar v1, IRTemp addr, UChar m3) +{ + IRExpr* maxIndex = binop(Iop_Sub32, + s390_getCountToBlockBoundary(addr, m3), + mkU32(1)); + + s390_vr_loadWithLength(v1, addr, maxIndex); + + return "vlbb"; +} + +static const HChar * +s390_irgen_VLL(UChar v1, IRTemp addr, UChar r3) +{ + s390_vr_loadWithLength(v1, addr, get_gpr_w1(r3)); + + return "vll"; +} + +static const HChar * +s390_irgen_VSTL(UChar v1, IRTemp addr, UChar r3) +{ + IRTemp counter = newTemp(Ity_I64); + IRTemp maxIndexToStore = newTemp(Ity_I64); + IRTemp gpr3 = newTemp(Ity_I64); + + assign(gpr3, unop(Iop_32Uto64, get_gpr_w1(r3))); + assign(maxIndexToStore, mkite(binop(Iop_CmpLE64U, + mkexpr(gpr3), + mkU64(16) + ), + mkexpr(gpr3), + mkU64(16) + ) + ); + + assign(counter, get_counter_dw0()); + + store(binop(Iop_Add64, mkexpr(addr), mkexpr(counter)), + binop(Iop_GetElem8x16, get_vr_qw(v1), unop(Iop_64to8, mkexpr(counter)))); + + /* Check for end of field */ + put_counter_dw0(binop(Iop_Add64, mkexpr(counter), mkU64(1))); + iterate_if(binop(Iop_CmpNE64, mkexpr(counter), mkexpr(maxIndexToStore))); + put_counter_dw0(mkU64(0)); + + return "vstl"; +} + +static const HChar * +s390_irgen_VX(UChar v1, UChar v2, UChar v3) +{ + put_vr_qw(v1, binop(Iop_XorV128, get_vr_qw(v2), get_vr_qw(v3))); + + return "vx"; +} + +static const HChar * +s390_irgen_VN(UChar v1, UChar v2, UChar v3) +{ + put_vr_qw(v1, binop(Iop_AndV128, get_vr_qw(v2), get_vr_qw(v3))); + + return "vn"; +} + +static const HChar * +s390_irgen_VO(UChar v1, UChar v2, UChar v3) +{ + put_vr_qw(v1, binop(Iop_OrV128, get_vr_qw(v2), get_vr_qw(v3))); + + return "vo"; +} + +static const HChar * +s390_irgen_VNO(UChar v1, UChar v2, UChar v3) +{ + put_vr_qw(v1, unop(Iop_NotV128, + binop(Iop_OrV128, get_vr_qw(v2), get_vr_qw(v3)))); + + return "vno"; +} + + +/* New insns are added here. + If an insn is contingent on a facility being installed also + check whether the list of supported facilities in function + s390x_dirtyhelper_STFLE needs updating */ + +/*------------------------------------------------------------*/ +/*--- Build IR for special instructions ---*/ +/*------------------------------------------------------------*/ + +static void +s390_irgen_client_request(void) +{ + if (0) + vex_printf("%%R3 = client_request ( %%R2 )\n"); + + Addr64 next = guest_IA_curr_instr + S390_SPECIAL_OP_PREAMBLE_SIZE + + S390_SPECIAL_OP_SIZE; + + dis_res->jk_StopHere = Ijk_ClientReq; + dis_res->whatNext = Dis_StopHere; + + put_IA(mkaddr_expr(next)); +} + +static void +s390_irgen_guest_NRADDR(void) { if (0) vex_printf("%%R3 = guest_NRADDR\n"); @@ -15016,6 +16579,7 @@ s390_decode_4byte_and_irgen(const UChar *bytes) case 0xb927: s390_format_RRE_RR(s390_irgen_LHR, ovl.fmt.RRE.r1, ovl.fmt.RRE.r2); goto ok; case 0xb928: /* PCKMO */ goto unimplemented; + case 0xb929: /* KMA */ goto unimplemented; case 0xb92a: /* KMF */ goto unimplemented; case 0xb92b: /* KMO */ goto unimplemented; case 0xb92c: /* PCC */ goto unimplemented; @@ -15026,6 +16590,7 @@ s390_decode_4byte_and_irgen(const UChar *bytes) ... [truncated message content] |