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