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_DEBUG_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_
18#define ART_COMPILER_DEBUG_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_
19
20#include "base/bit_utils.h"
21#include "debug/dwarf/dwarf_constants.h"
22#include "debug/dwarf/register.h"
23#include "debug/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 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
38 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.
47  void ALWAYS_INLINE AdvancePC(int absolute_pc) {
48    DCHECK_GE(absolute_pc, current_pc_);
49    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        }
64      }
65      current_pc_ = absolute_pc;
66    }
67  }
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.
73  void ALWAYS_INLINE RelOffset(Reg reg, int offset) {
74    Offset(reg, offset - current_cfa_offset_);
75  }
76
77  // Common alias in assemblers - increase stack frame size.
78  void ALWAYS_INLINE AdjustCFAOffset(int delta) {
79    DefCFAOffset(current_cfa_offset_ + delta);
80  }
81
82  // Custom alias - spill many registers based on bitmask.
83  void ALWAYS_INLINE RelOffsetForMany(Reg reg_base, int offset,
84                                      uint32_t reg_mask, int reg_size) {
85    DCHECK(reg_size == 4 || reg_size == 8);
86    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;
92        RelOffset(Reg(reg_base.num() + i), offset);
93        offset += reg_size;
94      }
95    }
96  }
97
98  // Custom alias - unspill many registers based on bitmask.
99  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;
106        Restore(Reg(reg_base.num() + i));
107      }
108    }
109  }
110
111  void ALWAYS_INLINE Nop() {
112    if (UNLIKELY(enabled_)) {
113      this->PushUint8(DW_CFA_nop);
114    }
115  }
116
117  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        }
130      } else {
131        uses_dwarf3_features_ = true;
132        this->PushUint8(DW_CFA_offset_extended_sf);
133        this->PushUleb128(reg.num());
134        this->PushSleb128(factored_offset);
135      }
136    }
137  }
138
139  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);
155      this->PushUleb128(reg.num());
156    }
157  }
158
159  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    }
165  }
166
167  // The previous value of "reg" is stored in register "new_reg".
168  void ALWAYS_INLINE Register(Reg reg, Reg new_reg) {
169    if (UNLIKELY(enabled_)) {
170      ImplicitlyAdvancePC();
171      this->PushUint8(DW_CFA_register);
172      this->PushUleb128(reg.num());
173      this->PushUleb128(new_reg.num());
174    }
175  }
176
177  void ALWAYS_INLINE RememberState() {
178    if (UNLIKELY(enabled_)) {
179      ImplicitlyAdvancePC();
180      this->PushUint8(DW_CFA_remember_state);
181    }
182  }
183
184  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_)) {
193      ImplicitlyAdvancePC();
194      if (offset >= 0) {
195        this->PushUint8(DW_CFA_def_cfa);
196        this->PushUleb128(reg.num());
197        this->PushUleb128(offset);  // Non-factored.
198      } else {
199        uses_dwarf3_features_ = true;
200        this->PushUint8(DW_CFA_def_cfa_sf);
201        this->PushUleb128(reg.num());
202        this->PushSleb128(FactorDataOffset(offset));
203      }
204    }
205    current_cfa_offset_ = offset;
206  }
207
208  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
251  void ALWAYS_INLINE DefCFAExpression(uint8_t* expr, int expr_size) {
252    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
261  void ALWAYS_INLINE Expression(Reg reg, uint8_t* expr, int expr_size) {
262    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
272  void ALWAYS_INLINE ValExpression(Reg reg, uint8_t* expr, int expr_size) {
273    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
285  void SetEnabled(bool value) {
286    enabled_ = value;
287    if (enabled_ && opcodes_.capacity() == 0u) {
288      opcodes_.reserve(kDefaultCapacity);
289    }
290  }
291
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
298  using Writer<Vector>::data;
299
300  explicit DebugFrameOpCodeWriter(bool enabled = true,
301                                  const typename Vector::allocator_type& alloc =
302                                      typename Vector::allocator_type())
303      : Writer<Vector>(&opcodes_),
304        enabled_(false),
305        opcodes_(alloc),
306        current_cfa_offset_(0),
307        current_pc_(0),
308        uses_dwarf3_features_(false) {
309    SetEnabled(enabled);
310  }
311
312  virtual ~DebugFrameOpCodeWriter() { }
313
314 protected:
315  // Best guess based on couple of observed outputs.
316  static constexpr size_t kDefaultCapacity = 32u;
317
318  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
328  bool enabled_;  // If disabled all writes are no-ops.
329  Vector opcodes_;
330  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
341#endif  // ART_COMPILER_DEBUG_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_
342