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 25 namespace art { 26 namespace 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. 34 template<typename Allocator = std::allocator<uint8_t> > 35 class 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. AdvancePC(int absolute_pc)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. ImplicitlyAdvancePC()68 virtual void ImplicitlyAdvancePC() { } 69 70 // Common alias in assemblers - spill relative to current stack pointer. RelOffset(Reg reg,int offset)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. AdjustCFAOffset(int delta)76 void ALWAYS_INLINE AdjustCFAOffset(int delta) { 77 DefCFAOffset(current_cfa_offset_ + delta); 78 } 79 80 // Custom alias - spill many registers based on bitmask. RelOffsetForMany(Reg reg_base,int offset,uint32_t reg_mask,int reg_size)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. RestoreMany(Reg reg_base,uint32_t reg_mask)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 Nop()109 void ALWAYS_INLINE Nop() { 110 if (UNLIKELY(enabled_)) { 111 this->PushUint8(DW_CFA_nop); 112 } 113 } 114 Offset(Reg reg,int offset)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 Restore(Reg reg)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 Undefined(Reg reg)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 SameValue(Reg reg)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". Register(Reg reg,Reg 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 RememberState()175 void ALWAYS_INLINE RememberState() { 176 if (UNLIKELY(enabled_)) { 177 ImplicitlyAdvancePC(); 178 this->PushUint8(DW_CFA_remember_state); 179 } 180 } 181 RestoreState()182 void ALWAYS_INLINE RestoreState() { 183 if (UNLIKELY(enabled_)) { 184 ImplicitlyAdvancePC(); 185 this->PushUint8(DW_CFA_restore_state); 186 } 187 } 188 DefCFA(Reg reg,int offset)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 DefCFARegister(Reg reg)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 DefCFAOffset(int offset)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 ValOffset(Reg reg,int offset)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 DefCFAExpression(void * expr,int expr_size)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 Expression(Reg reg,void * expr,int expr_size)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 ValExpression(Reg reg,void * expr,int expr_size)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 IsEnabled()281 bool IsEnabled() const { return enabled_; } 282 SetEnabled(bool value)283 void SetEnabled(bool value) { enabled_ = value; } 284 GetCurrentPC()285 int GetCurrentPC() const { return current_pc_; } 286 GetCurrentCFAOffset()287 int GetCurrentCFAOffset() const { return current_cfa_offset_; } 288 SetCurrentCFAOffset(int offset)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 ~DebugFrameOpCodeWriter()307 virtual ~DebugFrameOpCodeWriter() { } 308 309 protected: FactorDataOffset(int offset)310 int FactorDataOffset(int offset) const { 311 DCHECK_EQ(offset % kDataAlignmentFactor, 0); 312 return offset / kDataAlignmentFactor; 313 } 314 FactorCodeOffset(int offset)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