1/*
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
20#include "base/bit_utils.h"
21#include "dwarf/dwarf_constants.h"
22#include "dwarf/register.h"
23#include "dwarf/writer.h"
24
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.
45  void ALWAYS_INLINE AdvancePC(int absolute_pc) {
46    DCHECK_GE(absolute_pc, current_pc_);
47    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        }
62      }
63      current_pc_ = absolute_pc;
64    }
65  }
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.
71  void ALWAYS_INLINE RelOffset(Reg reg, int offset) {
72    Offset(reg, offset - current_cfa_offset_);
73  }
74
75  // Common alias in assemblers - increase stack frame size.
76  void ALWAYS_INLINE AdjustCFAOffset(int delta) {
77    DefCFAOffset(current_cfa_offset_ + delta);
78  }
79
80  // Custom alias - spill many registers based on bitmask.
81  void ALWAYS_INLINE RelOffsetForMany(Reg reg_base, int offset,
82                                      uint32_t reg_mask, int reg_size) {
83    DCHECK(reg_size == 4 || reg_size == 8);
84    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;
90        RelOffset(Reg(reg_base.num() + i), offset);
91        offset += reg_size;
92      }
93    }
94  }
95
96  // Custom alias - unspill many registers based on bitmask.
97  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;
104        Restore(Reg(reg_base.num() + i));
105      }
106    }
107  }
108
109  void ALWAYS_INLINE Nop() {
110    if (UNLIKELY(enabled_)) {
111      this->PushUint8(DW_CFA_nop);
112    }
113  }
114
115  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        }
128      } else {
129        uses_dwarf3_features_ = true;
130        this->PushUint8(DW_CFA_offset_extended_sf);
131        this->PushUleb128(reg.num());
132        this->PushSleb128(factored_offset);
133      }
134    }
135  }
136
137  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);
153      this->PushUleb128(reg.num());
154    }
155  }
156
157  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    }
163  }
164
165  // The previous value of "reg" is stored in register "new_reg".
166  void ALWAYS_INLINE Register(Reg reg, Reg new_reg) {
167    if (UNLIKELY(enabled_)) {
168      ImplicitlyAdvancePC();
169      this->PushUint8(DW_CFA_register);
170      this->PushUleb128(reg.num());
171      this->PushUleb128(new_reg.num());
172    }
173  }
174
175  void ALWAYS_INLINE RememberState() {
176    if (UNLIKELY(enabled_)) {
177      ImplicitlyAdvancePC();
178      this->PushUint8(DW_CFA_remember_state);
179    }
180  }
181
182  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_)) {
191      ImplicitlyAdvancePC();
192      if (offset >= 0) {
193        this->PushUint8(DW_CFA_def_cfa);
194        this->PushUleb128(reg.num());
195        this->PushUleb128(offset);  // Non-factored.
196      } else {
197        uses_dwarf3_features_ = true;
198        this->PushUint8(DW_CFA_def_cfa_sf);
199        this->PushUleb128(reg.num());
200        this->PushSleb128(FactorDataOffset(offset));
201      }
202    }
203    current_cfa_offset_ = offset;
204  }
205
206  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
291  using Writer<Allocator>::data;
292
293  DebugFrameOpCodeWriter(bool enabled = true,
294                         const Allocator& alloc = Allocator())
295      : Writer<Allocator>(&opcodes_),
296        enabled_(enabled),
297        opcodes_(alloc),
298        current_cfa_offset_(0),
299        current_pc_(0),
300        uses_dwarf3_features_(false) {
301    if (enabled) {
302      // Best guess based on couple of observed outputs.
303      opcodes_.reserve(16);
304    }
305  }
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
320  bool enabled_;  // If disabled all writes are no-ops.
321  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_
334