1 /* Jitter: data locations.
2 
3    Copyright (C) 2019, 2020 Luca Saiu
4    Written by Luca Saiu
5 
6    This file is part of Jitter.
7 
8    Jitter is free software: you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation, either version 3 of the License, or
11    (at your option) any later version.
12 
13    Jitter is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with Jitter.  If not, see <http://www.gnu.org/licenses/>. */
20 
21 
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #include <jitter/jitter-data-locations.h>
26 #include <jitter/jitter-malloc.h>
27 #include <jitter/jitter-fatal.h>
28 #include <jitter/jitter-specialize.h> /* For special specialized instructions. */
29 
30 
31 /* Low-level debugging features relying on assembly: data locations.
32  * ************************************************************************** */
33 
34 /* Given a location as a string, return non-false iff the location represents a
35    register. */
36 static bool
jitter_data_location_is_register(const char * location)37 jitter_data_location_is_register (const char *location)
38 {
39   /* Take some characters which can only occur within memory operands in
40      assembly notation.  This default covers every architecture I know of, but
41      if some architecture requires something different it suffices to define the
42      macro JITTER_MEMORY_OPERAND_DISTINGUISHING_CHARACTERS in the appropriate
43      machine.h file to support it. */
44   char *memory_only_characters
45 #ifdef JITTER_MEMORY_OPERAND_DISTINGUISHING_CHARACTERS
46     = JITTER_MEMORY_OPERAND_DISTINGUISHING_CHARACTERS
47 #else
48     = "[(@";
49 #endif
50   size_t memory_only_characters_no = strlen (memory_only_characters);
51 
52   /* Check every memory-only character.  If the string contains any of them,
53      then it represents memory.  If it contains none of them, it represents a
54      register. */
55   int i;
56   for (i = 0; i < memory_only_characters_no; i ++)
57     if (strchr (location, memory_only_characters [i]) != NULL)
58       return false;
59   return true;
60 }
61 
62 struct jitter_data_locations *
jitter_make_data_locations(const struct jitter_vm * vm)63 jitter_make_data_locations (const struct jitter_vm *vm)
64 {
65   const char *s;
66   size_t string_length;
67   size_t string_no = 0;
68 #ifndef JITTER_DISPATCH_SWITCH
69   /* First pass: find how many entries there are, by counting non-empty strings
70      up to the final empty string used as a terminator.  They must come in an
71      even number, since each entry contains one name and one location. */
72   s = vm->data_locations;
73   while ((string_length = strlen (s)) != 0)
74     {
75       s += string_length + 1;
76       string_no ++;
77     }
78   if (string_no % 2 != 0)
79     jitter_fatal ("impossible: data locations are odd in number");
80 #endif // #ifndef JITTER_DISPATCH_SWITCH
81   size_t entry_no = string_no / 2;
82 
83   /* Allocate the result.  The actual strings point to memory from the constant
84      bytes emitted by inline assembly, so there is no need to allocate them
85      dynamically. */
86   struct jitter_data_locations *res
87     = jitter_xmalloc (sizeof (struct jitter_data_locations));
88   res->data_locations
89     = jitter_xmalloc (entry_no * sizeof (struct jitter_data_location));
90   res->data_location_no = entry_no;
91 
92   /* Second pass: fill entries in the result array. */
93   bool name = true;
94   struct jitter_data_location *location = res->data_locations;
95 #ifndef JITTER_DISPATCH_SWITCH
96   s = vm->data_locations;
97 #else // switch dispatch
98   s = ""; /* End immediately. */
99 #endif // #ifndef JITTER_DISPATCH_SWITCH
100   while ((string_length = strlen (s)) != 0)
101     {
102       if (name)
103         location->name = s;
104       else
105         {
106           location->location = s;
107           location->register_
108             = jitter_data_location_is_register (location->location);
109           location ++;
110         }
111       s += string_length + 1;
112       string_no ++;
113       name = ! name;
114     }
115 
116   /* The result is reliable as long as the !DATALOCATIONS special specialized
117      instruction has the same size as the !NOP special specialized instruction.
118      In other words, there must be no loads or moves in the compiled code for
119      !DATALOCATIONS . */
120   res->reliable
121 #ifndef JITTER_DISPATCH_SWITCH
122     = (vm->thread_sizes [jitter_specialized_instruction_opcode_DATALOCATIONS]
123        == vm->thread_sizes [jitter_specialized_instruction_opcode_NOP]);
124 #else  // switch dispatch
125     = true;
126 #endif // #ifndef JITTER_DISPATCH_SWITCH
127 
128   /* Done. */
129   return res;
130 }
131 
132 void
jitter_destroy_data_locations(struct jitter_data_locations * locations)133 jitter_destroy_data_locations (struct jitter_data_locations *locations)
134 {
135   free (locations->data_locations);
136   free (locations);
137 }
138 
139 
140 
141 
142 /* Data locations: human-readable output.
143  * ************************************************************************** */
144 
145 /* Begin using a class in the given print context, where the class name is
146    formed by the concatenation of the lower-case prefix for the VM of the
147    pointed executable routine, concatenated to a dash, concatenated
148    to the given suffix.
149    For example, if the mutable routine r belonged to a VM named "foo",
150      jitter_disassemble_begin_class (ctx, r, "label")
151    would open a class in the context ctx named "foo-label". */
152 __attribute__ ((unused))
153 static void
jitter_locations_begin_class(jitter_print_context ctx,const struct jitter_vm * vm,const char * suffix)154 jitter_locations_begin_class (jitter_print_context ctx,
155                               const struct jitter_vm *vm,
156                               const char *suffix)
157 {
158   char *prefix = vm->configuration->lower_case_prefix;
159   size_t size = strlen (prefix) + 1 + strlen (suffix) + 1;
160   char *buffer = jitter_xmalloc (size);
161   sprintf (buffer, "%s-%s", prefix, suffix);
162   jitter_print_begin_class (ctx, buffer);
163   free (buffer);
164 }
165 
166 void
jitter_dump_data_locations(jitter_print_context out,const struct jitter_vm * vm)167 jitter_dump_data_locations (jitter_print_context out,
168                             const struct jitter_vm *vm)
169 {
170   struct jitter_data_locations *locations = jitter_make_data_locations (vm);
171   if (! locations->reliable)
172     {
173       jitter_print_char_star (out, "The following information is unreliable: at least\n");
174       jitter_print_char_star (out, "one datum needs more than one load instruction to be\n");
175       jitter_print_char_star (out, "accessed.\n");
176       if (JITTER_ARCHITECTURE_IS ("sh"))
177         {
178           jitter_print_char_star (out, "This might happen, on SH, because of the\n");
179           jitter_print_char_star (out, "restricted load offset ranges.\n");
180         }
181     }
182   int i;
183   size_t register_no = 0;
184   for (i = 0; i < locations->data_location_no; i ++)
185     {
186       char s [1000];
187       sprintf (s, "%2i. %24s: %-12s (%s)\n",
188                i,
189                locations->data_locations [i].name,
190                locations->data_locations [i].location,
191                locations->data_locations [i].register_ ? "register" : "memory");
192       jitter_print_char_star (out, s);
193       if (locations->data_locations [i].register_)
194         register_no ++;
195     }
196   if (locations->data_location_no > 0)
197     {
198       int register_percentage
199         = (register_no * 100) / locations->data_location_no;
200       jitter_print_char_star (out, "Register ratio: ");
201       jitter_print_int (out, 10, register_percentage);
202       jitter_print_char_star (out, "%\n");
203     }
204   else
205     jitter_print_char_star (out, "Register ratio: undefined\n");
206   jitter_destroy_data_locations (locations);
207 }
208