blob: 4112c84027a1d44225b883e8739f84ca9eb535fd [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
17#ifndef ART_COMPILER_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_
18#define ART_COMPILER_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_
19
David Srbecky7c869b32015-04-12 08:47:47 +010020#include "dwarf/dwarf_constants.h"
21#include "dwarf/register.h"
22#include "dwarf/writer.h"
David Srbeckyc6b4dd82015-04-07 20:32:43 +010023#include "utils.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.
34template<typename Allocator = std::allocator<uint8_t> >
35class DebugFrameOpCodeWriter : private Writer<Allocator> {
36 public:
37 // To save space, DWARF divides most offsets by header-defined factors.
38 // They are used in integer divisions, so we make them constants.
39 // We usually subtract from stack base pointer, so making the factor
40 // negative makes the encoded values positive and thus easier to encode.
41 static constexpr int kDataAlignmentFactor = -4;
42 static constexpr int kCodeAlignmentFactor = 1;
43
44 // Explicitely advance the program counter to given location.
David Srbeckyc6b4dd82015-04-07 20:32:43 +010045 void ALWAYS_INLINE AdvancePC(int absolute_pc) {
David Srbecky15c19752015-03-31 14:53:55 +000046 DCHECK_GE(absolute_pc, current_pc_);
David Srbeckyc6b4dd82015-04-07 20:32:43 +010047 if (UNLIKELY(enabled_)) {
48 int delta = FactorCodeOffset(absolute_pc - current_pc_);
49 if (delta != 0) {
50 if (delta <= 0x3F) {
51 this->PushUint8(DW_CFA_advance_loc | delta);
52 } else if (delta <= UINT8_MAX) {
53 this->PushUint8(DW_CFA_advance_loc1);
54 this->PushUint8(delta);
55 } else if (delta <= UINT16_MAX) {
56 this->PushUint8(DW_CFA_advance_loc2);
57 this->PushUint16(delta);
58 } else {
59 this->PushUint8(DW_CFA_advance_loc4);
60 this->PushUint32(delta);
61 }
David Srbecky15c19752015-03-31 14:53:55 +000062 }
David Srbeckyc6b4dd82015-04-07 20:32:43 +010063 current_pc_ = absolute_pc;
David Srbecky15c19752015-03-31 14:53:55 +000064 }
David Srbecky15c19752015-03-31 14:53:55 +000065 }
66
67 // Override this method to automatically advance the PC before each opcode.
68 virtual void ImplicitlyAdvancePC() { }
69
70 // Common alias in assemblers - spill relative to current stack pointer.
David Srbeckyc6b4dd82015-04-07 20:32:43 +010071 void ALWAYS_INLINE RelOffset(Reg reg, int offset) {
David Srbecky15c19752015-03-31 14:53:55 +000072 Offset(reg, offset - current_cfa_offset_);
73 }
74
75 // Common alias in assemblers - increase stack frame size.
David Srbeckyc6b4dd82015-04-07 20:32:43 +010076 void ALWAYS_INLINE AdjustCFAOffset(int delta) {
David Srbecky15c19752015-03-31 14:53:55 +000077 DefCFAOffset(current_cfa_offset_ + delta);
78 }
79
80 // Custom alias - spill many registers based on bitmask.
David Srbeckyc6b4dd82015-04-07 20:32:43 +010081 void ALWAYS_INLINE RelOffsetForMany(Reg reg_base, int offset,
82 uint32_t reg_mask, int reg_size) {
David Srbecky15c19752015-03-31 14:53:55 +000083 DCHECK(reg_size == 4 || reg_size == 8);
David Srbeckyc6b4dd82015-04-07 20:32:43 +010084 if (UNLIKELY(enabled_)) {
85 for (int i = 0; reg_mask != 0u; reg_mask >>= 1, i++) {
86 // Skip zero bits and go to the set bit.
87 int num_zeros = CTZ(reg_mask);
88 i += num_zeros;
89 reg_mask >>= num_zeros;
David Srbecky15c19752015-03-31 14:53:55 +000090 RelOffset(Reg(reg_base.num() + i), offset);
91 offset += reg_size;
92 }
93 }
94 }
95
96 // Custom alias - unspill many registers based on bitmask.
David Srbeckyc6b4dd82015-04-07 20:32:43 +010097 void ALWAYS_INLINE RestoreMany(Reg reg_base, uint32_t reg_mask) {
98 if (UNLIKELY(enabled_)) {
99 for (int i = 0; reg_mask != 0u; reg_mask >>= 1, i++) {
100 // Skip zero bits and go to the set bit.
101 int num_zeros = CTZ(reg_mask);
102 i += num_zeros;
103 reg_mask >>= num_zeros;
David Srbecky15c19752015-03-31 14:53:55 +0000104 Restore(Reg(reg_base.num() + i));
105 }
106 }
107 }
108
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100109 void ALWAYS_INLINE Nop() {
110 if (UNLIKELY(enabled_)) {
111 this->PushUint8(DW_CFA_nop);
112 }
David Srbecky15c19752015-03-31 14:53:55 +0000113 }
114
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100115 void ALWAYS_INLINE Offset(Reg reg, int offset) {
116 if (UNLIKELY(enabled_)) {
117 ImplicitlyAdvancePC();
118 int factored_offset = FactorDataOffset(offset); // May change sign.
119 if (factored_offset >= 0) {
120 if (0 <= reg.num() && reg.num() <= 0x3F) {
121 this->PushUint8(DW_CFA_offset | reg.num());
122 this->PushUleb128(factored_offset);
123 } else {
124 this->PushUint8(DW_CFA_offset_extended);
125 this->PushUleb128(reg.num());
126 this->PushUleb128(factored_offset);
127 }
David Srbecky15c19752015-03-31 14:53:55 +0000128 } else {
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100129 uses_dwarf3_features_ = true;
130 this->PushUint8(DW_CFA_offset_extended_sf);
David Srbecky15c19752015-03-31 14:53:55 +0000131 this->PushUleb128(reg.num());
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100132 this->PushSleb128(factored_offset);
David Srbecky15c19752015-03-31 14:53:55 +0000133 }
David Srbecky15c19752015-03-31 14:53:55 +0000134 }
135 }
136
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100137 void ALWAYS_INLINE Restore(Reg reg) {
138 if (UNLIKELY(enabled_)) {
139 ImplicitlyAdvancePC();
140 if (0 <= reg.num() && reg.num() <= 0x3F) {
141 this->PushUint8(DW_CFA_restore | reg.num());
142 } else {
143 this->PushUint8(DW_CFA_restore_extended);
144 this->PushUleb128(reg.num());
145 }
146 }
147 }
148
149 void ALWAYS_INLINE Undefined(Reg reg) {
150 if (UNLIKELY(enabled_)) {
151 ImplicitlyAdvancePC();
152 this->PushUint8(DW_CFA_undefined);
David Srbecky15c19752015-03-31 14:53:55 +0000153 this->PushUleb128(reg.num());
154 }
155 }
156
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100157 void ALWAYS_INLINE SameValue(Reg reg) {
158 if (UNLIKELY(enabled_)) {
159 ImplicitlyAdvancePC();
160 this->PushUint8(DW_CFA_same_value);
161 this->PushUleb128(reg.num());
162 }
David Srbecky15c19752015-03-31 14:53:55 +0000163 }
164
165 // The previous value of "reg" is stored in register "new_reg".
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100166 void ALWAYS_INLINE Register(Reg reg, Reg new_reg) {
167 if (UNLIKELY(enabled_)) {
168 ImplicitlyAdvancePC();
169 this->PushUint8(DW_CFA_register);
David Srbecky15c19752015-03-31 14:53:55 +0000170 this->PushUleb128(reg.num());
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100171 this->PushUleb128(new_reg.num());
David Srbecky15c19752015-03-31 14:53:55 +0000172 }
David Srbecky15c19752015-03-31 14:53:55 +0000173 }
174
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100175 void ALWAYS_INLINE RememberState() {
176 if (UNLIKELY(enabled_)) {
177 ImplicitlyAdvancePC();
178 this->PushUint8(DW_CFA_remember_state);
179 }
David Srbecky15c19752015-03-31 14:53:55 +0000180 }
181
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100182 void ALWAYS_INLINE RestoreState() {
183 if (UNLIKELY(enabled_)) {
184 ImplicitlyAdvancePC();
185 this->PushUint8(DW_CFA_restore_state);
186 }
187 }
188
189 void ALWAYS_INLINE DefCFA(Reg reg, int offset) {
190 if (UNLIKELY(enabled_)) {
David Srbecky15c19752015-03-31 14:53:55 +0000191 ImplicitlyAdvancePC();
192 if (offset >= 0) {
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100193 this->PushUint8(DW_CFA_def_cfa);
194 this->PushUleb128(reg.num());
David Srbecky15c19752015-03-31 14:53:55 +0000195 this->PushUleb128(offset); // Non-factored.
196 } else {
197 uses_dwarf3_features_ = true;
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100198 this->PushUint8(DW_CFA_def_cfa_sf);
199 this->PushUleb128(reg.num());
David Srbecky15c19752015-03-31 14:53:55 +0000200 this->PushSleb128(FactorDataOffset(offset));
201 }
David Srbecky15c19752015-03-31 14:53:55 +0000202 }
David Srbecky15c19752015-03-31 14:53:55 +0000203 current_cfa_offset_ = offset;
204 }
205
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100206 void ALWAYS_INLINE DefCFARegister(Reg reg) {
207 if (UNLIKELY(enabled_)) {
208 ImplicitlyAdvancePC();
209 this->PushUint8(DW_CFA_def_cfa_register);
210 this->PushUleb128(reg.num());
211 }
212 }
213
214 void ALWAYS_INLINE DefCFAOffset(int offset) {
215 if (UNLIKELY(enabled_)) {
216 if (current_cfa_offset_ != offset) {
217 ImplicitlyAdvancePC();
218 if (offset >= 0) {
219 this->PushUint8(DW_CFA_def_cfa_offset);
220 this->PushUleb128(offset); // Non-factored.
221 } else {
222 uses_dwarf3_features_ = true;
223 this->PushUint8(DW_CFA_def_cfa_offset_sf);
224 this->PushSleb128(FactorDataOffset(offset));
225 }
226 }
227 }
228 // Uncoditional so that the user can still get and check the value.
229 current_cfa_offset_ = offset;
230 }
231
232 void ALWAYS_INLINE ValOffset(Reg reg, int offset) {
233 if (UNLIKELY(enabled_)) {
234 ImplicitlyAdvancePC();
235 uses_dwarf3_features_ = true;
236 int factored_offset = FactorDataOffset(offset); // May change sign.
237 if (factored_offset >= 0) {
238 this->PushUint8(DW_CFA_val_offset);
239 this->PushUleb128(reg.num());
240 this->PushUleb128(factored_offset);
241 } else {
242 this->PushUint8(DW_CFA_val_offset_sf);
243 this->PushUleb128(reg.num());
244 this->PushSleb128(factored_offset);
245 }
246 }
247 }
248
249 void ALWAYS_INLINE DefCFAExpression(void * expr, int expr_size) {
250 if (UNLIKELY(enabled_)) {
251 ImplicitlyAdvancePC();
252 uses_dwarf3_features_ = true;
253 this->PushUint8(DW_CFA_def_cfa_expression);
254 this->PushUleb128(expr_size);
255 this->PushData(expr, expr_size);
256 }
257 }
258
259 void ALWAYS_INLINE Expression(Reg reg, void * expr, int expr_size) {
260 if (UNLIKELY(enabled_)) {
261 ImplicitlyAdvancePC();
262 uses_dwarf3_features_ = true;
263 this->PushUint8(DW_CFA_expression);
264 this->PushUleb128(reg.num());
265 this->PushUleb128(expr_size);
266 this->PushData(expr, expr_size);
267 }
268 }
269
270 void ALWAYS_INLINE ValExpression(Reg reg, void * expr, int expr_size) {
271 if (UNLIKELY(enabled_)) {
272 ImplicitlyAdvancePC();
273 uses_dwarf3_features_ = true;
274 this->PushUint8(DW_CFA_val_expression);
275 this->PushUleb128(reg.num());
276 this->PushUleb128(expr_size);
277 this->PushData(expr, expr_size);
278 }
279 }
280
281 bool IsEnabled() const { return enabled_; }
282
283 void SetEnabled(bool value) { enabled_ = value; }
284
285 int GetCurrentPC() const { return current_pc_; }
286
287 int GetCurrentCFAOffset() const { return current_cfa_offset_; }
288
289 void SetCurrentCFAOffset(int offset) { current_cfa_offset_ = offset; }
290
David Srbecky15c19752015-03-31 14:53:55 +0000291 using Writer<Allocator>::data;
292
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100293 DebugFrameOpCodeWriter(bool enabled = true,
294 const Allocator& alloc = Allocator())
David Srbecky15c19752015-03-31 14:53:55 +0000295 : Writer<Allocator>(&opcodes_),
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100296 enabled_(enabled),
David Srbecky15c19752015-03-31 14:53:55 +0000297 opcodes_(alloc),
298 current_cfa_offset_(0),
299 current_pc_(0),
300 uses_dwarf3_features_(false) {
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100301 if (enabled) {
302 // Best guess based on couple of observed outputs.
303 opcodes_.reserve(16);
304 }
David Srbecky15c19752015-03-31 14:53:55 +0000305 }
306
307 virtual ~DebugFrameOpCodeWriter() { }
308
309 protected:
310 int FactorDataOffset(int offset) const {
311 DCHECK_EQ(offset % kDataAlignmentFactor, 0);
312 return offset / kDataAlignmentFactor;
313 }
314
315 int FactorCodeOffset(int offset) const {
316 DCHECK_EQ(offset % kCodeAlignmentFactor, 0);
317 return offset / kCodeAlignmentFactor;
318 }
319
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100320 bool enabled_; // If disabled all writes are no-ops.
David Srbecky15c19752015-03-31 14:53:55 +0000321 std::vector<uint8_t, Allocator> opcodes_;
322 int current_cfa_offset_;
323 int current_pc_;
324 bool uses_dwarf3_features_;
325
326 private:
327 DISALLOW_COPY_AND_ASSIGN(DebugFrameOpCodeWriter);
328};
329
330} // namespace dwarf
331} // namespace art
332
333#endif // ART_COMPILER_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_