blob: 7c75c9bf376b4bb78946c058aaef5230afc76173 [file] [log] [blame]
David Srbecky15c19752015-03-31 14:53:55 +00001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
David Srbecky4fda4eb2016-02-05 13:34:46 +000017#ifndef ART_COMPILER_DEBUG_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_
18#define ART_COMPILER_DEBUG_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_
David Srbecky15c19752015-03-31 14:53:55 +000019
Vladimir Marko80afd022015-05-19 18:08:00 +010020#include "base/bit_utils.h"
David Srbecky4fda4eb2016-02-05 13:34:46 +000021#include "debug/dwarf/dwarf_constants.h"
22#include "debug/dwarf/register.h"
23#include "debug/dwarf/writer.h"
David Srbecky15c19752015-03-31 14:53:55 +000024
25namespace art {
26namespace dwarf {
27
28// Writer for .debug_frame opcodes (DWARF-3).
29// See the DWARF specification for the precise meaning of the opcodes.
30// The writer is very light-weight, however it will do the following for you:
31// * Choose the most compact encoding of a given opcode.
32// * Keep track of current state and convert absolute values to deltas.
33// * Divide by header-defined factors as appropriate.
Vladimir Markoec7802a2015-10-01 20:57:57 +010034template<typename Vector = std::vector<uint8_t> >
35class DebugFrameOpCodeWriter : private Writer<Vector> {
36 static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type");
37
David Srbecky15c19752015-03-31 14:53:55 +000038 public:
39 // To save space, DWARF divides most offsets by header-defined factors.
40 // They are used in integer divisions, so we make them constants.
41 // We usually subtract from stack base pointer, so making the factor
42 // negative makes the encoded values positive and thus easier to encode.
43 static constexpr int kDataAlignmentFactor = -4;
44 static constexpr int kCodeAlignmentFactor = 1;
45
46 // Explicitely advance the program counter to given location.
David Srbeckyc6b4dd82015-04-07 20:32:43 +010047 void ALWAYS_INLINE AdvancePC(int absolute_pc) {
David Srbecky15c19752015-03-31 14:53:55 +000048 DCHECK_GE(absolute_pc, current_pc_);
David Srbeckyc6b4dd82015-04-07 20:32:43 +010049 if (UNLIKELY(enabled_)) {
50 int delta = FactorCodeOffset(absolute_pc - current_pc_);
51 if (delta != 0) {
52 if (delta <= 0x3F) {
53 this->PushUint8(DW_CFA_advance_loc | delta);
54 } else if (delta <= UINT8_MAX) {
55 this->PushUint8(DW_CFA_advance_loc1);
56 this->PushUint8(delta);
57 } else if (delta <= UINT16_MAX) {
58 this->PushUint8(DW_CFA_advance_loc2);
59 this->PushUint16(delta);
60 } else {
61 this->PushUint8(DW_CFA_advance_loc4);
62 this->PushUint32(delta);
63 }
David Srbecky15c19752015-03-31 14:53:55 +000064 }
David Srbeckyc6b4dd82015-04-07 20:32:43 +010065 current_pc_ = absolute_pc;
David Srbecky15c19752015-03-31 14:53:55 +000066 }
David Srbecky15c19752015-03-31 14:53:55 +000067 }
68
69 // Override this method to automatically advance the PC before each opcode.
70 virtual void ImplicitlyAdvancePC() { }
71
72 // Common alias in assemblers - spill relative to current stack pointer.
David Srbeckyc6b4dd82015-04-07 20:32:43 +010073 void ALWAYS_INLINE RelOffset(Reg reg, int offset) {
David Srbecky15c19752015-03-31 14:53:55 +000074 Offset(reg, offset - current_cfa_offset_);
75 }
76
77 // Common alias in assemblers - increase stack frame size.
David Srbeckyc6b4dd82015-04-07 20:32:43 +010078 void ALWAYS_INLINE AdjustCFAOffset(int delta) {
David Srbecky15c19752015-03-31 14:53:55 +000079 DefCFAOffset(current_cfa_offset_ + delta);
80 }
81
82 // Custom alias - spill many registers based on bitmask.
David Srbeckyc6b4dd82015-04-07 20:32:43 +010083 void ALWAYS_INLINE RelOffsetForMany(Reg reg_base, int offset,
84 uint32_t reg_mask, int reg_size) {
David Srbecky15c19752015-03-31 14:53:55 +000085 DCHECK(reg_size == 4 || reg_size == 8);
David Srbeckyc6b4dd82015-04-07 20:32:43 +010086 if (UNLIKELY(enabled_)) {
87 for (int i = 0; reg_mask != 0u; reg_mask >>= 1, i++) {
88 // Skip zero bits and go to the set bit.
89 int num_zeros = CTZ(reg_mask);
90 i += num_zeros;
91 reg_mask >>= num_zeros;
David Srbecky15c19752015-03-31 14:53:55 +000092 RelOffset(Reg(reg_base.num() + i), offset);
93 offset += reg_size;
94 }
95 }
96 }
97
98 // Custom alias - unspill many registers based on bitmask.
David Srbeckyc6b4dd82015-04-07 20:32:43 +010099 void ALWAYS_INLINE RestoreMany(Reg reg_base, uint32_t reg_mask) {
100 if (UNLIKELY(enabled_)) {
101 for (int i = 0; reg_mask != 0u; reg_mask >>= 1, i++) {
102 // Skip zero bits and go to the set bit.
103 int num_zeros = CTZ(reg_mask);
104 i += num_zeros;
105 reg_mask >>= num_zeros;
David Srbecky15c19752015-03-31 14:53:55 +0000106 Restore(Reg(reg_base.num() + i));
107 }
108 }
109 }
110
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100111 void ALWAYS_INLINE Nop() {
112 if (UNLIKELY(enabled_)) {
113 this->PushUint8(DW_CFA_nop);
114 }
David Srbecky15c19752015-03-31 14:53:55 +0000115 }
116
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100117 void ALWAYS_INLINE Offset(Reg reg, int offset) {
118 if (UNLIKELY(enabled_)) {
119 ImplicitlyAdvancePC();
120 int factored_offset = FactorDataOffset(offset); // May change sign.
121 if (factored_offset >= 0) {
122 if (0 <= reg.num() && reg.num() <= 0x3F) {
123 this->PushUint8(DW_CFA_offset | reg.num());
124 this->PushUleb128(factored_offset);
125 } else {
126 this->PushUint8(DW_CFA_offset_extended);
127 this->PushUleb128(reg.num());
128 this->PushUleb128(factored_offset);
129 }
David Srbecky15c19752015-03-31 14:53:55 +0000130 } else {
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100131 uses_dwarf3_features_ = true;
132 this->PushUint8(DW_CFA_offset_extended_sf);
David Srbecky15c19752015-03-31 14:53:55 +0000133 this->PushUleb128(reg.num());
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100134 this->PushSleb128(factored_offset);
David Srbecky15c19752015-03-31 14:53:55 +0000135 }
David Srbecky15c19752015-03-31 14:53:55 +0000136 }
137 }
138
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100139 void ALWAYS_INLINE Restore(Reg reg) {
140 if (UNLIKELY(enabled_)) {
141 ImplicitlyAdvancePC();
142 if (0 <= reg.num() && reg.num() <= 0x3F) {
143 this->PushUint8(DW_CFA_restore | reg.num());
144 } else {
145 this->PushUint8(DW_CFA_restore_extended);
146 this->PushUleb128(reg.num());
147 }
148 }
149 }
150
151 void ALWAYS_INLINE Undefined(Reg reg) {
152 if (UNLIKELY(enabled_)) {
153 ImplicitlyAdvancePC();
154 this->PushUint8(DW_CFA_undefined);
David Srbecky15c19752015-03-31 14:53:55 +0000155 this->PushUleb128(reg.num());
156 }
157 }
158
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100159 void ALWAYS_INLINE SameValue(Reg reg) {
160 if (UNLIKELY(enabled_)) {
161 ImplicitlyAdvancePC();
162 this->PushUint8(DW_CFA_same_value);
163 this->PushUleb128(reg.num());
164 }
David Srbecky15c19752015-03-31 14:53:55 +0000165 }
166
167 // The previous value of "reg" is stored in register "new_reg".
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100168 void ALWAYS_INLINE Register(Reg reg, Reg new_reg) {
169 if (UNLIKELY(enabled_)) {
170 ImplicitlyAdvancePC();
171 this->PushUint8(DW_CFA_register);
David Srbecky15c19752015-03-31 14:53:55 +0000172 this->PushUleb128(reg.num());
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100173 this->PushUleb128(new_reg.num());
David Srbecky15c19752015-03-31 14:53:55 +0000174 }
David Srbecky15c19752015-03-31 14:53:55 +0000175 }
176
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100177 void ALWAYS_INLINE RememberState() {
178 if (UNLIKELY(enabled_)) {
179 ImplicitlyAdvancePC();
180 this->PushUint8(DW_CFA_remember_state);
181 }
David Srbecky15c19752015-03-31 14:53:55 +0000182 }
183
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100184 void ALWAYS_INLINE RestoreState() {
185 if (UNLIKELY(enabled_)) {
186 ImplicitlyAdvancePC();
187 this->PushUint8(DW_CFA_restore_state);
188 }
189 }
190
191 void ALWAYS_INLINE DefCFA(Reg reg, int offset) {
192 if (UNLIKELY(enabled_)) {
David Srbecky15c19752015-03-31 14:53:55 +0000193 ImplicitlyAdvancePC();
194 if (offset >= 0) {
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100195 this->PushUint8(DW_CFA_def_cfa);
196 this->PushUleb128(reg.num());
David Srbecky15c19752015-03-31 14:53:55 +0000197 this->PushUleb128(offset); // Non-factored.
198 } else {
199 uses_dwarf3_features_ = true;
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100200 this->PushUint8(DW_CFA_def_cfa_sf);
201 this->PushUleb128(reg.num());
David Srbecky15c19752015-03-31 14:53:55 +0000202 this->PushSleb128(FactorDataOffset(offset));
203 }
David Srbecky15c19752015-03-31 14:53:55 +0000204 }
David Srbecky15c19752015-03-31 14:53:55 +0000205 current_cfa_offset_ = offset;
206 }
207
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100208 void ALWAYS_INLINE DefCFARegister(Reg reg) {
209 if (UNLIKELY(enabled_)) {
210 ImplicitlyAdvancePC();
211 this->PushUint8(DW_CFA_def_cfa_register);
212 this->PushUleb128(reg.num());
213 }
214 }
215
216 void ALWAYS_INLINE DefCFAOffset(int offset) {
217 if (UNLIKELY(enabled_)) {
218 if (current_cfa_offset_ != offset) {
219 ImplicitlyAdvancePC();
220 if (offset >= 0) {
221 this->PushUint8(DW_CFA_def_cfa_offset);
222 this->PushUleb128(offset); // Non-factored.
223 } else {
224 uses_dwarf3_features_ = true;
225 this->PushUint8(DW_CFA_def_cfa_offset_sf);
226 this->PushSleb128(FactorDataOffset(offset));
227 }
228 }
229 }
230 // Uncoditional so that the user can still get and check the value.
231 current_cfa_offset_ = offset;
232 }
233
234 void ALWAYS_INLINE ValOffset(Reg reg, int offset) {
235 if (UNLIKELY(enabled_)) {
236 ImplicitlyAdvancePC();
237 uses_dwarf3_features_ = true;
238 int factored_offset = FactorDataOffset(offset); // May change sign.
239 if (factored_offset >= 0) {
240 this->PushUint8(DW_CFA_val_offset);
241 this->PushUleb128(reg.num());
242 this->PushUleb128(factored_offset);
243 } else {
244 this->PushUint8(DW_CFA_val_offset_sf);
245 this->PushUleb128(reg.num());
246 this->PushSleb128(factored_offset);
247 }
248 }
249 }
250
David Srbecky91cb54e2016-01-15 13:47:59 +0000251 void ALWAYS_INLINE DefCFAExpression(uint8_t* expr, int expr_size) {
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100252 if (UNLIKELY(enabled_)) {
253 ImplicitlyAdvancePC();
254 uses_dwarf3_features_ = true;
255 this->PushUint8(DW_CFA_def_cfa_expression);
256 this->PushUleb128(expr_size);
257 this->PushData(expr, expr_size);
258 }
259 }
260
David Srbecky91cb54e2016-01-15 13:47:59 +0000261 void ALWAYS_INLINE Expression(Reg reg, uint8_t* expr, int expr_size) {
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100262 if (UNLIKELY(enabled_)) {
263 ImplicitlyAdvancePC();
264 uses_dwarf3_features_ = true;
265 this->PushUint8(DW_CFA_expression);
266 this->PushUleb128(reg.num());
267 this->PushUleb128(expr_size);
268 this->PushData(expr, expr_size);
269 }
270 }
271
David Srbecky91cb54e2016-01-15 13:47:59 +0000272 void ALWAYS_INLINE ValExpression(Reg reg, uint8_t* expr, int expr_size) {
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100273 if (UNLIKELY(enabled_)) {
274 ImplicitlyAdvancePC();
275 uses_dwarf3_features_ = true;
276 this->PushUint8(DW_CFA_val_expression);
277 this->PushUleb128(reg.num());
278 this->PushUleb128(expr_size);
279 this->PushData(expr, expr_size);
280 }
281 }
282
283 bool IsEnabled() const { return enabled_; }
284
Vladimir Marko10ef6942015-10-22 15:25:54 +0100285 void SetEnabled(bool value) {
286 enabled_ = value;
287 if (enabled_ && opcodes_.capacity() == 0u) {
288 opcodes_.reserve(kDefaultCapacity);
289 }
290 }
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100291
292 int GetCurrentPC() const { return current_pc_; }
293
294 int GetCurrentCFAOffset() const { return current_cfa_offset_; }
295
296 void SetCurrentCFAOffset(int offset) { current_cfa_offset_ = offset; }
297
Vladimir Markoec7802a2015-10-01 20:57:57 +0100298 using Writer<Vector>::data;
David Srbecky15c19752015-03-31 14:53:55 +0000299
Vladimir Marko10ef6942015-10-22 15:25:54 +0100300 explicit DebugFrameOpCodeWriter(bool enabled = true,
301 const typename Vector::allocator_type& alloc =
302 typename Vector::allocator_type())
Vladimir Markoec7802a2015-10-01 20:57:57 +0100303 : Writer<Vector>(&opcodes_),
Vladimir Marko10ef6942015-10-22 15:25:54 +0100304 enabled_(false),
David Srbecky15c19752015-03-31 14:53:55 +0000305 opcodes_(alloc),
306 current_cfa_offset_(0),
307 current_pc_(0),
308 uses_dwarf3_features_(false) {
Vladimir Marko10ef6942015-10-22 15:25:54 +0100309 SetEnabled(enabled);
David Srbecky15c19752015-03-31 14:53:55 +0000310 }
311
312 virtual ~DebugFrameOpCodeWriter() { }
313
314 protected:
Vladimir Marko10ef6942015-10-22 15:25:54 +0100315 // Best guess based on couple of observed outputs.
316 static constexpr size_t kDefaultCapacity = 32u;
317
David Srbecky15c19752015-03-31 14:53:55 +0000318 int FactorDataOffset(int offset) const {
319 DCHECK_EQ(offset % kDataAlignmentFactor, 0);
320 return offset / kDataAlignmentFactor;
321 }
322
323 int FactorCodeOffset(int offset) const {
324 DCHECK_EQ(offset % kCodeAlignmentFactor, 0);
325 return offset / kCodeAlignmentFactor;
326 }
327
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100328 bool enabled_; // If disabled all writes are no-ops.
Vladimir Markoec7802a2015-10-01 20:57:57 +0100329 Vector opcodes_;
David Srbecky15c19752015-03-31 14:53:55 +0000330 int current_cfa_offset_;
331 int current_pc_;
332 bool uses_dwarf3_features_;
333
334 private:
335 DISALLOW_COPY_AND_ASSIGN(DebugFrameOpCodeWriter);
336};
337
338} // namespace dwarf
339} // namespace art
340
David Srbecky4fda4eb2016-02-05 13:34:46 +0000341#endif // ART_COMPILER_DEBUG_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_