blob: 6f8e08b3bad62a298c5234d4e8a9f6fb05cdabf4 [file] [log] [blame]
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001/*
2 * Copyright (C) 2012 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#include "disassembler_arm.h"
18
Ian Rogersef7d42f2014-01-06 12:55:46 -080019#include <inttypes.h>
20
Ian Rogers3a5c1ce2012-02-29 10:06:46 -080021#include <iostream>
22
Elliott Hughes07ed66b2012-12-12 18:34:25 -080023#include "base/logging.h"
Elliott Hughese222ee02012-12-13 14:41:43 -080024#include "base/stringprintf.h"
Elliott Hughes28fa76d2012-04-09 17:31:46 -070025#include "thread.h"
Elliott Hughes0f3c5532012-03-30 14:51:51 -070026
Ian Rogers3a5c1ce2012-02-29 10:06:46 -080027namespace art {
28namespace arm {
29
Ian Rogersb23a7722012-10-09 16:54:26 -070030size_t DisassemblerArm::Dump(std::ostream& os, const uint8_t* begin) {
31 if ((reinterpret_cast<intptr_t>(begin) & 1) == 0) {
32 DumpArm(os, begin);
33 return 4;
34 } else {
35 // remove thumb specifier bits
36 begin = reinterpret_cast<const uint8_t*>(reinterpret_cast<uintptr_t>(begin) & ~1);
37 return DumpThumb16(os, begin);
38 }
39}
40
Ian Rogers3a5c1ce2012-02-29 10:06:46 -080041void DisassemblerArm::Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end) {
42 if ((reinterpret_cast<intptr_t>(begin) & 1) == 0) {
43 for (const uint8_t* cur = begin; cur < end; cur += 4) {
44 DumpArm(os, cur);
45 }
46 } else {
47 // remove thumb specifier bits
48 begin = reinterpret_cast<const uint8_t*>(reinterpret_cast<uintptr_t>(begin) & ~1);
49 end = reinterpret_cast<const uint8_t*>(reinterpret_cast<uintptr_t>(end) & ~1);
50 for (const uint8_t* cur = begin; cur < end;) {
51 cur += DumpThumb16(os, cur);
52 }
53 }
54}
55
Elliott Hughes77405792012-03-15 15:22:12 -070056static const char* kConditionCodeNames[] = {
Elliott Hughescbf0b612012-03-15 16:23:47 -070057 "eq", // 0000 - equal
58 "ne", // 0001 - not-equal
59 "cs", // 0010 - carry-set, greater than, equal or unordered
60 "cc", // 0011 - carry-clear, less than
61 "mi", // 0100 - minus, negative
62 "pl", // 0101 - plus, positive or zero
63 "vs", // 0110 - overflow
64 "vc", // 0111 - no overflow
65 "hi", // 1000 - unsigned higher
66 "ls", // 1001 - unsigned lower or same
67 "ge", // 1010 - signed greater than or equal
68 "lt", // 1011 - signed less than
69 "gt", // 1100 - signed greater than
70 "le", // 1101 - signed less than or equal
71 "", // 1110 - always
72 "nv", // 1111 - never (mostly obsolete, but might be a clue that we're mistranslating)
Ian Rogers40627db2012-03-04 17:31:09 -080073};
74
75void DisassemblerArm::DumpCond(std::ostream& os, uint32_t cond) {
76 if (cond < 15) {
Elliott Hughes77405792012-03-15 15:22:12 -070077 os << kConditionCodeNames[cond];
Ian Rogers40627db2012-03-04 17:31:09 -080078 } else {
79 os << "Unexpected condition: " << cond;
80 }
81}
82
Ian Rogersb122a4b2013-11-19 18:00:50 -080083void DisassemblerArm::DumpMemoryDomain(std::ostream& os, uint32_t domain) {
84 switch (domain) {
Andreas Gampec8ccf682014-09-29 20:07:43 -070085 case 15U /* 0b1111 */: os << "sy"; break;
86 case 14U /* 0b1110 */: os << "st"; break;
87 case 11U /* 0b1011 */: os << "ish"; break;
88 case 10U /* 0b1010 */: os << "ishst"; break;
89 case 7U /* 0b0111 */: os << "nsh"; break;
90 case 6U /* 0b0110 */: os << "nshst"; break;
91 case 3U /* 0b0011 */: os << "osh"; break;
92 case 2U /* 0b0010 */: os << "oshst"; break;
Ian Rogersb122a4b2013-11-19 18:00:50 -080093 }
94}
95
Ian Rogers40627db2012-03-04 17:31:09 -080096void DisassemblerArm::DumpBranchTarget(std::ostream& os, const uint8_t* instr_ptr, int32_t imm32) {
Brian Carlstrom2cbaccb2014-09-14 20:34:17 -070097 os << StringPrintf("%+d (", imm32) << FormatInstructionPointer(instr_ptr + imm32) << ")";
Ian Rogers3a5c1ce2012-02-29 10:06:46 -080098}
99
100static uint32_t ReadU16(const uint8_t* ptr) {
101 return ptr[0] | (ptr[1] << 8);
102}
103
104static uint32_t ReadU32(const uint8_t* ptr) {
105 return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
106}
107
Elliott Hughes77405792012-03-15 15:22:12 -0700108static const char* kDataProcessingOperations[] = {
Elliott Hughescbf0b612012-03-15 16:23:47 -0700109 "and", "eor", "sub", "rsb", "add", "adc", "sbc", "rsc",
110 "tst", "teq", "cmp", "cmn", "orr", "mov", "bic", "mvn",
Elliott Hughes77405792012-03-15 15:22:12 -0700111};
112
Ian Rogersad03ef52012-03-18 19:34:47 -0700113static const char* kThumbDataProcessingOperations[] = {
114 "and", "eor", "lsl", "lsr", "asr", "adc", "sbc", "ror",
115 "tst", "rsb", "cmp", "cmn", "orr", "mul", "bic", "mvn",
116};
117
Vladimir Markoc777e0d2014-04-03 17:59:02 +0100118static const char* const kThumb2ShiftOperations[] = {
119 "lsl", "lsr", "asr", "ror"
120};
121
Vladimir Markoa8b4caf2013-10-24 15:08:57 +0100122static const char* kThumbReverseOperations[] = {
123 "rev", "rev16", "rbit", "revsh"
124};
125
Elliott Hughes77405792012-03-15 15:22:12 -0700126struct ArmRegister {
Elliott Hughes74847412012-06-20 18:10:21 -0700127 explicit ArmRegister(uint32_t r) : r(r) { CHECK_LE(r, 15U); }
Elliott Hughes630e77d2012-03-22 19:20:56 -0700128 ArmRegister(uint32_t instruction, uint32_t at_bit) : r((instruction >> at_bit) & 0xf) { CHECK_LE(r, 15U); }
Elliott Hughes77405792012-03-15 15:22:12 -0700129 uint32_t r;
130};
131std::ostream& operator<<(std::ostream& os, const ArmRegister& r) {
132 if (r.r == 13) {
Elliott Hughescbf0b612012-03-15 16:23:47 -0700133 os << "sp";
Elliott Hughes77405792012-03-15 15:22:12 -0700134 } else if (r.r == 14) {
Elliott Hughescbf0b612012-03-15 16:23:47 -0700135 os << "lr";
Elliott Hughes77405792012-03-15 15:22:12 -0700136 } else if (r.r == 15) {
Elliott Hughescbf0b612012-03-15 16:23:47 -0700137 os << "pc";
Elliott Hughes77405792012-03-15 15:22:12 -0700138 } else {
Elliott Hughescbf0b612012-03-15 16:23:47 -0700139 os << "r" << r.r;
Elliott Hughes77405792012-03-15 15:22:12 -0700140 }
141 return os;
142}
143
Elliott Hughes630e77d2012-03-22 19:20:56 -0700144struct ThumbRegister : ArmRegister {
145 ThumbRegister(uint16_t instruction, uint16_t at_bit) : ArmRegister((instruction >> at_bit) & 0x7) {}
Elliott Hughes77405792012-03-15 15:22:12 -0700146};
147
148struct Rm {
Elliott Hughes74847412012-06-20 18:10:21 -0700149 explicit Rm(uint32_t instruction) : shift((instruction >> 4) & 0xff), rm(instruction & 0xf) {}
Elliott Hughes77405792012-03-15 15:22:12 -0700150 uint32_t shift;
151 ArmRegister rm;
152};
153std::ostream& operator<<(std::ostream& os, const Rm& r) {
154 os << r.rm;
155 if (r.shift != 0) {
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700156 os << "-shift-" << r.shift; // TODO
Elliott Hughes77405792012-03-15 15:22:12 -0700157 }
158 return os;
159}
160
Elliott Hughes1ca98492012-04-12 17:21:02 -0700161struct ShiftedImmediate {
Elliott Hughes74847412012-06-20 18:10:21 -0700162 explicit ShiftedImmediate(uint32_t instruction) {
Elliott Hughes3d71d072012-04-10 18:28:35 -0700163 uint32_t rotate = ((instruction >> 8) & 0xf);
164 uint32_t imm = (instruction & 0xff);
165 value = (imm >> (2 * rotate)) | (imm << (32 - (2 * rotate)));
166 }
167 uint32_t value;
Elliott Hughes77405792012-03-15 15:22:12 -0700168};
Elliott Hughes1ca98492012-04-12 17:21:02 -0700169std::ostream& operator<<(std::ostream& os, const ShiftedImmediate& rhs) {
Elliott Hughes3d71d072012-04-10 18:28:35 -0700170 os << "#" << rhs.value;
Elliott Hughes77405792012-03-15 15:22:12 -0700171 return os;
172}
173
174struct RegisterList {
Elliott Hughes74847412012-06-20 18:10:21 -0700175 explicit RegisterList(uint32_t instruction) : register_list(instruction & 0xffff) {}
Elliott Hughes77405792012-03-15 15:22:12 -0700176 uint32_t register_list;
177};
178std::ostream& operator<<(std::ostream& os, const RegisterList& rhs) {
179 if (rhs.register_list == 0) {
180 os << "<no register list?>";
181 return os;
182 }
Elliott Hughes630e77d2012-03-22 19:20:56 -0700183 os << "{";
Elliott Hughes77405792012-03-15 15:22:12 -0700184 bool first = true;
185 for (size_t i = 0; i < 16; i++) {
186 if ((rhs.register_list & (1 << i)) != 0) {
187 if (first) {
Elliott Hughes77405792012-03-15 15:22:12 -0700188 first = false;
189 } else {
190 os << ", ";
191 }
192 os << ArmRegister(i);
193 }
194 }
195 os << "}";
196 return os;
197}
Ian Rogers3a5c1ce2012-02-29 10:06:46 -0800198
Vladimir Markodd577a32013-11-07 19:25:24 +0000199struct FpRegister {
200 explicit FpRegister(uint32_t instr, uint16_t at_bit, uint16_t extra_at_bit) {
201 size = (instr >> 8) & 1;
202 uint32_t Vn = (instr >> at_bit) & 0xF;
203 uint32_t N = (instr >> extra_at_bit) & 1;
204 r = (size != 0 ? ((N << 4) | Vn) : ((Vn << 1) | N));
205 }
Zheng Xue19649a2014-02-27 13:30:55 +0000206 explicit FpRegister(uint32_t instr, uint16_t at_bit, uint16_t extra_at_bit,
207 uint32_t forced_size) {
208 size = forced_size;
209 uint32_t Vn = (instr >> at_bit) & 0xF;
210 uint32_t N = (instr >> extra_at_bit) & 1;
211 r = (size != 0 ? ((N << 4) | Vn) : ((Vn << 1) | N));
212 }
Vladimir Markodd577a32013-11-07 19:25:24 +0000213 FpRegister(const FpRegister& other, uint32_t offset)
214 : size(other.size), r(other.r + offset) {}
215
216 uint32_t size; // 0 = f32, 1 = f64
217 uint32_t r;
218};
219std::ostream& operator<<(std::ostream& os, const FpRegister& rhs) {
220 return os << ((rhs.size != 0) ? "d" : "s") << rhs.r;
221}
222
223struct FpRegisterRange {
224 explicit FpRegisterRange(uint32_t instr)
225 : first(instr, 12, 22), imm8(instr & 0xFF) {}
226 FpRegister first;
227 uint32_t imm8;
228};
229std::ostream& operator<<(std::ostream& os, const FpRegisterRange& rhs) {
230 os << "{" << rhs.first;
231 int count = (rhs.first.size != 0 ? ((rhs.imm8 + 1u) >> 1) : rhs.imm8);
232 if (count > 1) {
233 os << "-" << FpRegister(rhs.first, count - 1);
234 }
235 if (rhs.imm8 == 0) {
236 os << " (EMPTY)";
237 } else if (rhs.first.size != 0 && (rhs.imm8 & 1) != 0) {
238 os << rhs.first << " (HALF)";
239 }
240 os << "}";
241 return os;
242}
243
Ian Rogers3a5c1ce2012-02-29 10:06:46 -0800244void DisassemblerArm::DumpArm(std::ostream& os, const uint8_t* instr_ptr) {
Elliott Hughes77405792012-03-15 15:22:12 -0700245 uint32_t instruction = ReadU32(instr_ptr);
246 uint32_t cond = (instruction >> 28) & 0xf;
247 uint32_t op1 = (instruction >> 25) & 0x7;
Elliott Hughes3d71d072012-04-10 18:28:35 -0700248 std::string opcode;
249 std::string suffixes;
Elliott Hughescbf0b612012-03-15 16:23:47 -0700250 std::ostringstream args;
Elliott Hughes77405792012-03-15 15:22:12 -0700251 switch (op1) {
252 case 0:
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700253 case 1: // Data processing instructions.
Elliott Hughes77405792012-03-15 15:22:12 -0700254 {
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700255 if ((instruction & 0x0ff000f0) == 0x01200070) { // BKPT
Elliott Hughes3d71d072012-04-10 18:28:35 -0700256 opcode = "bkpt";
257 uint32_t imm12 = (instruction >> 8) & 0xfff;
258 uint32_t imm4 = (instruction & 0xf);
259 args << '#' << ((imm12 << 4) | imm4);
260 break;
261 }
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700262 if ((instruction & 0x0fffffd0) == 0x012fff10) { // BX and BLX (register)
Elliott Hughes3d71d072012-04-10 18:28:35 -0700263 opcode = (((instruction >> 5) & 1) ? "blx" : "bx");
Elliott Hughescbf0b612012-03-15 16:23:47 -0700264 args << ArmRegister(instruction & 0xf);
Elliott Hughes77405792012-03-15 15:22:12 -0700265 break;
266 }
267 bool i = (instruction & (1 << 25)) != 0;
268 bool s = (instruction & (1 << 20)) != 0;
Elliott Hughes3d71d072012-04-10 18:28:35 -0700269 uint32_t op = (instruction >> 21) & 0xf;
270 opcode = kDataProcessingOperations[op];
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700271 bool implicit_s = ((op & ~3) == 8); // TST, TEQ, CMP, and CMN.
Andreas Gampec8ccf682014-09-29 20:07:43 -0700272 bool is_mov = op == 13U /* 0b1101 */ || op == 15U /* 0b1111 */;
Dave Allison20dfc792014-06-16 20:44:29 -0700273 if (is_mov) {
274 // Show only Rd and Rm.
Elliott Hughes3d71d072012-04-10 18:28:35 -0700275 if (s) {
Dave Allison20dfc792014-06-16 20:44:29 -0700276 suffixes += 's';
277 }
278 args << ArmRegister(instruction, 12) << ", ";
279 if (i) {
280 args << ShiftedImmediate(instruction);
281 } else {
282 // TODO: Shifted register.
283 args << ArmRegister(instruction, 16) << ", " << ArmRegister(instruction, 0);
284 }
Elliott Hughes77405792012-03-15 15:22:12 -0700285 } else {
Dave Allison20dfc792014-06-16 20:44:29 -0700286 if (implicit_s) {
287 // Rd is unused (and not shown), and we don't show the 's' suffix either.
288 } else {
289 if (s) {
290 suffixes += 's';
291 }
292 args << ArmRegister(instruction, 12) << ", ";
293 }
294 if (i) {
295 args << ArmRegister(instruction, 16) << ", " << ShiftedImmediate(instruction);
296 } else {
297 // TODO: Shifted register.
298 args << ArmRegister(instruction, 16) << ", " << ArmRegister(instruction, 0);
299 }
Elliott Hughes77405792012-03-15 15:22:12 -0700300 }
301 }
302 break;
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700303 case 2: // Load/store word and unsigned byte.
Elliott Hughes77405792012-03-15 15:22:12 -0700304 {
305 bool p = (instruction & (1 << 24)) != 0;
306 bool b = (instruction & (1 << 22)) != 0;
307 bool w = (instruction & (1 << 21)) != 0;
308 bool l = (instruction & (1 << 20)) != 0;
Elliott Hughes3d71d072012-04-10 18:28:35 -0700309 opcode = StringPrintf("%s%s", (l ? "ldr" : "str"), (b ? "b" : ""));
Elliott Hughes630e77d2012-03-22 19:20:56 -0700310 args << ArmRegister(instruction, 12) << ", ";
311 ArmRegister rn(instruction, 16);
312 if (rn.r == 0xf) {
Elliott Hughes77405792012-03-15 15:22:12 -0700313 UNIMPLEMENTED(FATAL) << "literals";
314 } else {
315 bool wback = !p || w;
Elliott Hughes1ca98492012-04-12 17:21:02 -0700316 uint32_t offset = (instruction & 0xfff);
Elliott Hughes77405792012-03-15 15:22:12 -0700317 if (p && !wback) {
Elliott Hughes1ca98492012-04-12 17:21:02 -0700318 args << "[" << rn << ", #" << offset << "]";
Elliott Hughes77405792012-03-15 15:22:12 -0700319 } else if (p && wback) {
Elliott Hughes1ca98492012-04-12 17:21:02 -0700320 args << "[" << rn << ", #" << offset << "]!";
Elliott Hughes77405792012-03-15 15:22:12 -0700321 } else if (!p && wback) {
Elliott Hughes1ca98492012-04-12 17:21:02 -0700322 args << "[" << rn << "], #" << offset;
Elliott Hughes77405792012-03-15 15:22:12 -0700323 } else {
324 LOG(FATAL) << p << " " << w;
325 }
Elliott Hughes3d71d072012-04-10 18:28:35 -0700326 if (rn.r == 9) {
327 args << " ; ";
Ian Rogersdd7624d2014-03-14 17:43:00 -0700328 Thread::DumpThreadOffset<4>(args, offset);
Elliott Hughes3d71d072012-04-10 18:28:35 -0700329 }
Elliott Hughes77405792012-03-15 15:22:12 -0700330 }
331 }
332 break;
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700333 case 4: // Load/store multiple.
Elliott Hughes77405792012-03-15 15:22:12 -0700334 {
335 bool p = (instruction & (1 << 24)) != 0;
336 bool u = (instruction & (1 << 23)) != 0;
337 bool w = (instruction & (1 << 21)) != 0;
338 bool l = (instruction & (1 << 20)) != 0;
Elliott Hughes3d71d072012-04-10 18:28:35 -0700339 opcode = StringPrintf("%s%c%c", (l ? "ldm" : "stm"), (u ? 'i' : 'd'), (p ? 'b' : 'a'));
Elliott Hughes630e77d2012-03-22 19:20:56 -0700340 args << ArmRegister(instruction, 16) << (w ? "!" : "") << ", " << RegisterList(instruction);
Elliott Hughes77405792012-03-15 15:22:12 -0700341 }
342 break;
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700343 case 5: // Branch/branch with link.
Elliott Hughes3d71d072012-04-10 18:28:35 -0700344 {
345 bool bl = (instruction & (1 << 24)) != 0;
346 opcode = (bl ? "bl" : "b");
Elliott Hughesd86261e2012-04-11 11:23:23 -0700347 int32_t imm26 = (instruction & 0xffffff) << 2;
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700348 int32_t imm32 = (imm26 << 6) >> 6; // Sign extend.
Elliott Hughes3d71d072012-04-10 18:28:35 -0700349 DumpBranchTarget(args, instr_ptr + 8, imm32);
350 }
351 break;
Elliott Hughes77405792012-03-15 15:22:12 -0700352 default:
Elliott Hughes3d71d072012-04-10 18:28:35 -0700353 opcode = "???";
Elliott Hughes77405792012-03-15 15:22:12 -0700354 break;
355 }
Elliott Hughes3d71d072012-04-10 18:28:35 -0700356 opcode += kConditionCodeNames[cond];
357 opcode += suffixes;
Elliott Hughescbf0b612012-03-15 16:23:47 -0700358 // TODO: a more complete ARM disassembler could generate wider opcodes.
Brian Carlstrom2cbaccb2014-09-14 20:34:17 -0700359 os << FormatInstructionPointer(instr_ptr)
360 << StringPrintf(": %08x\t%-7s ", instruction, opcode.c_str())
361 << args.str() << '\n';
Ian Rogers3a5c1ce2012-02-29 10:06:46 -0800362}
363
Ian Rogersa9650dd2013-10-04 08:23:32 -0700364int32_t ThumbExpand(int32_t imm12) {
365 if ((imm12 & 0xC00) == 0) {
366 switch ((imm12 >> 8) & 3) {
367 case 0:
368 return imm12 & 0xFF;
369 case 1:
370 return ((imm12 & 0xFF) << 16) | (imm12 & 0xFF);
371 case 2:
372 return ((imm12 & 0xFF) << 24) | ((imm12 & 0xFF) << 8);
373 default: // 3
374 return ((imm12 & 0xFF) << 24) | ((imm12 & 0xFF) << 16) | ((imm12 & 0xFF) << 8) |
375 (imm12 & 0xFF);
376 }
377 } else {
378 uint32_t val = 0x80 | (imm12 & 0x7F);
379 int32_t rotate = (imm12 >> 7) & 0x1F;
380 return (val >> rotate) | (val << (32 - rotate));
381 }
382}
383
Vladimir Markoc777e0d2014-04-03 17:59:02 +0100384uint32_t VFPExpand32(uint32_t imm8) {
385 CHECK_EQ(imm8 & 0xffu, imm8);
386 uint32_t bit_a = (imm8 >> 7) & 1;
387 uint32_t bit_b = (imm8 >> 6) & 1;
388 uint32_t slice = imm8 & 0x3f;
389 return (bit_a << 31) | ((1 << 30) - (bit_b << 25)) | (slice << 19);
390}
391
392uint64_t VFPExpand64(uint32_t imm8) {
393 CHECK_EQ(imm8 & 0xffu, imm8);
394 uint64_t bit_a = (imm8 >> 7) & 1;
395 uint64_t bit_b = (imm8 >> 6) & 1;
396 uint64_t slice = imm8 & 0x3f;
397 return (bit_a << 31) | ((UINT64_C(1) << 62) - (bit_b << 54)) | (slice << 48);
398}
399
400uint64_t AdvSIMDExpand(uint32_t op, uint32_t cmode, uint32_t imm8) {
401 CHECK_EQ(op & 1, op);
402 CHECK_EQ(cmode & 0xf, cmode);
403 CHECK_EQ(imm8 & 0xff, imm8);
404 int32_t cmode321 = cmode >> 1;
405 if (imm8 == 0 && cmode321 != 0 && cmode321 != 4 && cmode321 != 7) {
406 return INT64_C(0x00000000deadbeef); // UNPREDICTABLE
407 }
408 uint64_t imm = imm8;
409 switch (cmode321) {
410 case 3: imm <<= 8; // Fall through.
411 case 2: imm <<= 8; // Fall through.
412 case 1: imm <<= 8; // Fall through.
413 case 0: return static_cast<int64_t>((imm << 32) | imm);
414 case 5: imm <<= 8; // Fall through.
415 case 4: return static_cast<int64_t>((imm << 48) | (imm << 32) | (imm << 16) | imm);
416 case 6:
417 imm = ((imm + 1u) << ((cmode & 1) != 0 ? 16 : 8)) - 1u; // Add 8 or 16 ones.
418 return static_cast<int64_t>((imm << 32) | imm);
419 default:
420 CHECK_EQ(cmode321, 7);
421 if ((cmode & 1) == 0 && op == 0) {
422 imm = (imm << 8) | imm;
423 return static_cast<int64_t>((imm << 48) | (imm << 32) | (imm << 16) | imm);
424 } else if ((cmode & 1) == 0 && op != 0) {
425 for (int i = 1; i != 8; ++i) {
426 imm |= ((imm >> i) & UINT64_C(1)) << (i * 8);
427 }
428 imm = imm & ~UINT64_C(0xfe);
429 return static_cast<int64_t>((imm << 8) - imm);
430 } else if ((cmode & 1) != 0 && op == 0) {
431 imm = static_cast<uint32_t>(VFPExpand32(imm8));
432 return static_cast<int64_t>((imm << 32) | imm);
433 } else {
434 return INT64_C(0xdeadbeef00000000); // UNDEFINED
435 }
436 }
437}
438
Ian Rogers3a5c1ce2012-02-29 10:06:46 -0800439size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) {
440 uint32_t instr = (ReadU16(instr_ptr) << 16) | ReadU16(instr_ptr + 2);
441 // |111|1 1|1000000|0000|1111110000000000|
442 // |5 3|2 1|0987654|3 0|5 0 5 0|
443 // |---|---|-------|----|----------------|
444 // |332|2 2|2222222|1111|1111110000000000|
445 // |1 9|8 7|6543210|9 6|5 0 5 0|
446 // |---|---|-------|----|----------------|
447 // |111|op1| op2 | | |
448 uint32_t op1 = (instr >> 27) & 3;
Elliott Hughes77405792012-03-15 15:22:12 -0700449 if (op1 == 0) {
450 return DumpThumb16(os, instr_ptr);
451 }
452
Ian Rogers3a5c1ce2012-02-29 10:06:46 -0800453 uint32_t op2 = (instr >> 20) & 0x7F;
Elliott Hughescbf0b612012-03-15 16:23:47 -0700454 std::ostringstream opcode;
455 std::ostringstream args;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -0800456 switch (op1) {
457 case 0:
Ian Rogers3a5c1ce2012-02-29 10:06:46 -0800458 break;
459 case 1:
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700460 if ((op2 & 0x64) == 0) { // 00x x0xx
461 // |111|11|10|00|0|00|0000|1111110000000000|
462 // |5 3|21|09|87|6|54|3 0|5 0 5 0|
463 // |---|--|--|--|-|--|----|----------------|
464 // |332|22|22|22|2|22|1111|1111110000000000|
465 // |1 9|87|65|43|2|10|9 6|5 0 5 0|
466 // |---|--|--|--|-|--|----|----------------|
467 // |111|01|00|op|0|WL| Rn | |
468 // |111|01| op2 | | |
469 // STM - 111 01 00-01-0-W0 nnnn rrrrrrrrrrrrrrrr
470 // LDM - 111 01 00-01-0-W1 nnnn rrrrrrrrrrrrrrrr
471 // PUSH- 111 01 00-01-0-10 1101 0M0rrrrrrrrrrrrr
472 // POP - 111 01 00-01-0-11 1101 PM0rrrrrrrrrrrrr
473 uint32_t op = (instr >> 23) & 3;
474 uint32_t W = (instr >> 21) & 1;
475 uint32_t L = (instr >> 20) & 1;
476 ArmRegister Rn(instr, 16);
477 if (op == 1 || op == 2) {
478 if (op == 1) {
479 if (L == 0) {
480 opcode << "stm";
481 args << Rn << (W == 0 ? "" : "!") << ", ";
Ian Rogers3a5c1ce2012-02-29 10:06:46 -0800482 } else {
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700483 if (Rn.r != 13) {
484 opcode << "ldm";
Elliott Hughes630e77d2012-03-22 19:20:56 -0700485 args << Rn << (W == 0 ? "" : "!") << ", ";
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700486 } else {
487 opcode << "pop";
Ian Rogers3a5c1ce2012-02-29 10:06:46 -0800488 }
489 }
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700490 } else {
491 if (L == 0) {
492 if (Rn.r != 13) {
493 opcode << "stmdb";
494 args << Rn << (W == 0 ? "" : "!") << ", ";
495 } else {
496 opcode << "push";
497 }
498 } else {
499 opcode << "ldmdb";
500 args << Rn << (W == 0 ? "" : "!") << ", ";
501 }
Ian Rogers3a5c1ce2012-02-29 10:06:46 -0800502 }
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700503 args << RegisterList(instr);
Ian Rogers3a5c1ce2012-02-29 10:06:46 -0800504 }
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700505 } else if ((op2 & 0x64) == 4) { // 00x x1xx
Ian Rogers9af89402012-09-07 11:29:35 -0700506 uint32_t op3 = (instr >> 23) & 3;
507 uint32_t op4 = (instr >> 20) & 3;
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700508 // uint32_t op5 = (instr >> 4) & 0xF;
Ian Rogers9af89402012-09-07 11:29:35 -0700509 ArmRegister Rn(instr, 16);
510 ArmRegister Rt(instr, 12);
Dave Allison70202782013-10-22 17:52:19 -0700511 ArmRegister Rd(instr, 8);
Ian Rogers9af89402012-09-07 11:29:35 -0700512 uint32_t imm8 = instr & 0xFF;
Dave Allison70202782013-10-22 17:52:19 -0700513 if ((op3 & 2) == 2) { // 1x
514 int W = (instr >> 21) & 1;
515 int U = (instr >> 23) & 1;
516 int P = (instr >> 24) & 1;
517
518 if ((op4 & 1) == 1) {
519 opcode << "ldrd";
520 } else {
521 opcode << "strd";
522 }
523 args << Rt << "," << Rd << ", [" << Rn;
524 const char *sign = U ? "+" : "-";
525 if (P == 0 && W == 1) {
Vladimir Markoad435eb2013-11-15 15:21:25 +0000526 args << "], #" << sign << (imm8 << 2);
Dave Allison70202782013-10-22 17:52:19 -0700527 } else {
Vladimir Markoad435eb2013-11-15 15:21:25 +0000528 args << ", #" << sign << (imm8 << 2) << "]";
Dave Allison70202782013-10-22 17:52:19 -0700529 if (W == 1) {
530 args << "!";
531 }
532 }
533 } else { // 0x
534 switch (op4) {
535 case 0:
536 if (op3 == 0) { // op3 is 00, op4 is 00
537 opcode << "strex";
538 args << Rd << ", " << Rt << ", [" << Rn << ", #" << (imm8 << 2) << "]";
Vladimir Marko3e5af822013-11-21 15:01:20 +0000539 if (Rd.r == 13 || Rd.r == 15 || Rt.r == 13 || Rt.r == 15 || Rn.r == 15 ||
540 Rd.r == Rn.r || Rd.r == Rt.r) {
541 args << " (UNPREDICTABLE)";
542 }
Dave Allison70202782013-10-22 17:52:19 -0700543 } else { // op3 is 01, op4 is 00
544 // this is one of strexb, strexh or strexd
545 int op5 = (instr >> 4) & 0xf;
546 switch (op5) {
547 case 4:
Dave Allison70202782013-10-22 17:52:19 -0700548 case 5:
Vladimir Marko3e5af822013-11-21 15:01:20 +0000549 opcode << ((op5 == 4) ? "strexb" : "strexh");
550 Rd = ArmRegister(instr, 0);
551 args << Rd << ", " << Rt << ", [" << Rn << "]";
552 if (Rd.r == 13 || Rd.r == 15 || Rt.r == 13 || Rt.r == 15 || Rn.r == 15 ||
553 Rd.r == Rn.r || Rd.r == Rt.r || (instr & 0xf00) != 0xf00) {
554 args << " (UNPREDICTABLE)";
555 }
Dave Allison70202782013-10-22 17:52:19 -0700556 break;
557 case 7:
558 opcode << "strexd";
Vladimir Marko3e5af822013-11-21 15:01:20 +0000559 ArmRegister Rt2 = Rd;
560 Rd = ArmRegister(instr, 0);
561 args << Rd << ", " << Rt << ", " << Rt2 << ", [" << Rn << "]";
562 if (Rd.r == 13 || Rd.r == 15 || Rt.r == 13 || Rt.r == 15 ||
563 Rt2.r == 13 || Rt2.r == 15 || Rn.r == 15 ||
564 Rd.r == Rn.r || Rd.r == Rt.r || Rd.r == Rt2.r) {
565 args << " (UNPREDICTABLE)";
566 }
Dave Allison70202782013-10-22 17:52:19 -0700567 break;
568 }
569 }
570 break;
571 case 1:
572 if (op3 == 0) { // op3 is 00, op4 is 01
573 opcode << "ldrex";
574 args << Rt << ", [" << Rn << ", #" << (imm8 << 2) << "]";
Vladimir Marko3e5af822013-11-21 15:01:20 +0000575 if (Rt.r == 13 || Rt.r == 15 || Rn.r == 15 || (instr & 0xf00) != 0xf00) {
576 args << " (UNPREDICTABLE)";
577 }
Dave Allison70202782013-10-22 17:52:19 -0700578 } else { // op3 is 01, op4 is 01
579 // this is one of strexb, strexh or strexd
580 int op5 = (instr >> 4) & 0xf;
581 switch (op5) {
582 case 0:
583 opcode << "tbb";
584 break;
585 case 1:
586 opcode << "tbh";
587 break;
588 case 4:
Dave Allison70202782013-10-22 17:52:19 -0700589 case 5:
Vladimir Marko3e5af822013-11-21 15:01:20 +0000590 opcode << ((op5 == 4) ? "ldrexb" : "ldrexh");
591 args << Rt << ", [" << Rn << "]";
592 if (Rt.r == 13 || Rt.r == 15 || Rn.r == 15 || (instr & 0xf0f) != 0xf0f) {
593 args << " (UNPREDICTABLE)";
594 }
Dave Allison70202782013-10-22 17:52:19 -0700595 break;
596 case 7:
597 opcode << "ldrexd";
Vladimir Marko3e5af822013-11-21 15:01:20 +0000598 args << Rt << ", " << Rd /* Rt2 */ << ", [" << Rn << "]";
599 if (Rt.r == 13 || Rt.r == 15 || Rd.r == 13 /* Rt2 */ || Rd.r == 15 /* Rt2 */ ||
600 Rn.r == 15 || (instr & 0x00f) != 0x00f) {
601 args << " (UNPREDICTABLE)";
602 }
Dave Allison70202782013-10-22 17:52:19 -0700603 break;
604 }
605 }
606 break;
607 case 2: // op3 is 0x, op4 is 10
608 case 3: // op3 is 0x, op4 is 11
609 if (op4 == 2) {
610 opcode << "strd";
611 } else {
612 opcode << "ldrd";
613 }
614 int W = (instr >> 21) & 1;
615 int U = (instr >> 23) & 1;
616 int P = (instr >> 24) & 1;
617
618 args << Rt << "," << Rd << ", [" << Rn;
619 const char *sign = U ? "+" : "-";
620 if (P == 0 && W == 1) {
621 args << "], #" << sign << imm8;
622 } else {
623 args << ", #" << sign << imm8 << "]";
624 if (W == 1) {
625 args << "!";
626 }
627 }
628 break;
629 }
630 }
631
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700632 } else if ((op2 & 0x60) == 0x20) { // 01x xxxx
633 // Data-processing (shifted register)
Sebastien Hertzd9e63c02013-02-22 15:19:55 +0100634 // |111|1110|0000|0|0000|1111|1100|00|00|0000|
635 // |5 3|2109|8765|4|3 0|5 |10 8|7 |5 |3 0|
636 // |---|----|----|-|----|----|----|--|--|----|
637 // |332|2222|2222|2|1111|1111|1100|00|00|0000|
638 // |1 9|8765|4321|0|9 6|5 |10 8|7 |5 |3 0|
639 // |---|----|----|-|----|----|----|--|--|----|
640 // |111|0101| op3|S| Rn |imm3| Rd |i2|ty| Rm |
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700641 uint32_t op3 = (instr >> 21) & 0xF;
642 uint32_t S = (instr >> 20) & 1;
Sebastien Hertzd9e63c02013-02-22 15:19:55 +0100643 uint32_t imm3 = ((instr >> 12) & 0x7);
644 uint32_t imm2 = ((instr >> 6) & 0x3);
Dmitriy Ivanov7d180cb2014-03-25 10:31:04 -0700645 uint32_t imm5 = ((imm3 << 2) | imm2);
646 uint32_t shift_type = ((instr >> 4) & 0x3);
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700647 ArmRegister Rd(instr, 8);
Sebastien Hertzd9e63c02013-02-22 15:19:55 +0100648 ArmRegister Rn(instr, 16);
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700649 ArmRegister Rm(instr, 0);
650 switch (op3) {
651 case 0x0:
Sebastien Hertzd9e63c02013-02-22 15:19:55 +0100652 if (Rd.r != 0xF) {
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700653 opcode << "and";
654 } else {
Brian Carlstrom4a999e22013-03-11 16:57:09 -0700655 if (S != 1U) {
656 opcode << "UNKNOWN TST-" << S;
657 break;
658 }
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700659 opcode << "tst";
660 S = 0; // don't print 's'
661 }
662 break;
663 case 0x1: opcode << "bic"; break;
664 case 0x2:
Sebastien Hertzd9e63c02013-02-22 15:19:55 +0100665 if (Rn.r != 0xF) {
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700666 opcode << "orr";
667 } else {
Sebastien Hertzd9e63c02013-02-22 15:19:55 +0100668 // TODO: use canonical form if there is a shift (lsl, ...).
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700669 opcode << "mov";
670 }
671 break;
672 case 0x3:
Sebastien Hertzd9e63c02013-02-22 15:19:55 +0100673 if (Rn.r != 0xF) {
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700674 opcode << "orn";
675 } else {
676 opcode << "mvn";
677 }
678 break;
679 case 0x4:
Sebastien Hertzd9e63c02013-02-22 15:19:55 +0100680 if (Rd.r != 0xF) {
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700681 opcode << "eor";
682 } else {
Brian Carlstrom4a999e22013-03-11 16:57:09 -0700683 if (S != 1U) {
684 opcode << "UNKNOWN TEQ-" << S;
685 break;
686 }
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700687 opcode << "teq";
688 S = 0; // don't print 's'
689 }
690 break;
691 case 0x6: opcode << "pkh"; break;
692 case 0x8:
Sebastien Hertzd9e63c02013-02-22 15:19:55 +0100693 if (Rd.r != 0xF) {
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700694 opcode << "add";
695 } else {
Brian Carlstrom4a999e22013-03-11 16:57:09 -0700696 if (S != 1U) {
697 opcode << "UNKNOWN CMN-" << S;
698 break;
699 }
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700700 opcode << "cmn";
701 S = 0; // don't print 's'
702 }
703 break;
704 case 0xA: opcode << "adc"; break;
705 case 0xB: opcode << "sbc"; break;
Sebastien Hertzd9e63c02013-02-22 15:19:55 +0100706 case 0xD:
707 if (Rd.r != 0xF) {
708 opcode << "sub";
709 } else {
Brian Carlstrom4a999e22013-03-11 16:57:09 -0700710 if (S != 1U) {
711 opcode << "UNKNOWN CMP-" << S;
712 break;
713 }
Sebastien Hertzd9e63c02013-02-22 15:19:55 +0100714 opcode << "cmp";
Sebastien Hertzd9e63c02013-02-22 15:19:55 +0100715 S = 0; // don't print 's'
716 }
717 break;
718 case 0xE: opcode << "rsb"; break;
719 default: opcode << "UNKNOWN DPSR-" << op3; break;
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700720 }
Ian Rogers087b2412012-03-21 01:30:32 -0700721
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700722 if (S == 1) {
723 opcode << "s";
Ian Rogers087b2412012-03-21 01:30:32 -0700724 }
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700725 opcode << ".w";
Sebastien Hertzd9e63c02013-02-22 15:19:55 +0100726
727 if (Rd.r != 0xF) {
728 args << Rd << ", ";
729 }
730 if (Rn.r != 0xF) {
731 args << Rn << ", ";
732 }
733 args << Rm;
734
735 // Shift operand.
736 bool noShift = (imm5 == 0 && shift_type != 0x3);
737 if (!noShift) {
738 args << ", ";
739 switch (shift_type) {
740 case 0x0: args << "lsl"; break;
741 case 0x1: args << "lsr"; break;
742 case 0x2: args << "asr"; break;
743 case 0x3:
744 if (imm5 == 0) {
745 args << "rrx";
746 } else {
747 args << "ror";
748 }
749 break;
750 }
751 if (shift_type != 0x3 /* rrx */) {
Dmitriy Ivanov7d180cb2014-03-25 10:31:04 -0700752 args << StringPrintf(" #%d", (0 != imm5 || 0 == shift_type) ? imm5 : 32);
Sebastien Hertzd9e63c02013-02-22 15:19:55 +0100753 }
754 }
755
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700756 } else if ((op2 & 0x40) == 0x40) { // 1xx xxxx
757 // Co-processor instructions
758 // |111|1|11|000000|0000|1111|1100|000|0 |0000|
759 // |5 3|2|10|987654|3 0|54 2|10 8|7 5|4 | 0|
760 // |---|-|--|------|----|----|----|---|---|----|
761 // |332|2|22|222222|1111|1111|1100|000|0 |0000|
762 // |1 9|8|76|543210|9 6|54 2|10 8|7 5|4 | 0|
763 // |---|-|--|------|----|----|----|---|---|----|
764 // |111| |11| op3 | Rn | |copr| |op4| |
765 uint32_t op3 = (instr >> 20) & 0x3F;
766 uint32_t coproc = (instr >> 8) & 0xF;
767 uint32_t op4 = (instr >> 4) & 0x1;
Dave Allison70202782013-10-22 17:52:19 -0700768
Ian Rogersef6a7762013-12-19 17:58:05 -0800769 if (coproc == 0xA || coproc == 0xB) { // 101x
Vladimir Markodd577a32013-11-07 19:25:24 +0000770 if (op3 < 0x20 && (op3 & ~5) != 0) { // 0xxxxx and not 000x0x
771 // Extension register load/store instructions
772 // |1111|110|00000|0000|1111|110|0|00000000|
773 // |5 2|1 9|87654|3 0|5 2|1 9|8|7 0|
774 // |----|---|-----|----|----|---|-|--------|
775 // |3322|222|22222|1111|1111|110|0|00000000|
776 // |1 8|7 5|4 0|9 6|5 2|1 9|8|7 0|
777 // |----|---|-----|----|----|---|-|--------|
778 // |1110|110|PUDWL| Rn | Vd |101|S| imm8 |
Ian Rogers9af89402012-09-07 11:29:35 -0700779 uint32_t P = (instr >> 24) & 1;
780 uint32_t U = (instr >> 23) & 1;
Ian Rogers9af89402012-09-07 11:29:35 -0700781 uint32_t W = (instr >> 21) & 1;
Vladimir Markodd577a32013-11-07 19:25:24 +0000782 if (P == U && W == 1) {
783 opcode << "UNDEFINED";
784 } else {
785 uint32_t L = (instr >> 20) & 1;
786 uint32_t S = (instr >> 8) & 1;
787 ArmRegister Rn(instr, 16);
788 if (P == 1 && W == 0) { // VLDR
789 FpRegister d(instr, 12, 22);
790 uint32_t imm8 = instr & 0xFF;
791 opcode << (L == 1 ? "vldr" : "vstr");
792 args << d << ", [" << Rn << ", #" << ((U == 1) ? "" : "-")
793 << (imm8 << 2) << "]";
Ian Rogersef6a7762013-12-19 17:58:05 -0800794 if (Rn.r == 15 && U == 1) {
795 intptr_t lit_adr = reinterpret_cast<intptr_t>(instr_ptr);
796 lit_adr = RoundDown(lit_adr, 4) + 4 + (imm8 << 2);
Brian Carlstromc2687ef2014-03-13 15:12:11 -0700797 typedef const int64_t unaligned_int64_t __attribute__ ((aligned (2)));
798 args << StringPrintf(" ; 0x%" PRIx64, *reinterpret_cast<unaligned_int64_t*>(lit_adr));
Ian Rogersef6a7762013-12-19 17:58:05 -0800799 }
Vladimir Markodd577a32013-11-07 19:25:24 +0000800 } else if (Rn.r == 13 && W == 1 && U == L) { // VPUSH/VPOP
801 opcode << (L == 1 ? "vpop" : "vpush");
802 args << FpRegisterRange(instr);
803 } else { // VLDM
804 opcode << (L == 1 ? "vldm" : "vstm");
805 args << Rn << ((W == 1) ? "!" : "") << ", "
806 << FpRegisterRange(instr);
Dave Allison70202782013-10-22 17:52:19 -0700807 }
Vladimir Markodd577a32013-11-07 19:25:24 +0000808 opcode << (S == 1 ? ".f64" : ".f32");
Ian Rogers9af89402012-09-07 11:29:35 -0700809 }
Dave Allison70202782013-10-22 17:52:19 -0700810 } else if ((op3 >> 1) == 2) { // 00010x
Vladimir Markodd577a32013-11-07 19:25:24 +0000811 if ((instr & 0xD0) == 0x10) {
812 // 64bit transfers between ARM core and extension registers.
813 uint32_t L = (instr >> 20) & 1;
814 uint32_t S = (instr >> 8) & 1;
815 ArmRegister Rt2(instr, 16);
816 ArmRegister Rt(instr, 12);
817 FpRegister m(instr, 0, 5);
818 opcode << "vmov" << (S ? ".f64" : ".f32");
819 if (L == 1) {
820 args << Rt << ", " << Rt2 << ", ";
821 }
822 if (S) {
823 args << m;
824 } else {
825 args << m << ", " << FpRegister(m, 1);
826 }
827 if (L == 0) {
828 args << ", " << Rt << ", " << Rt2;
829 }
830 if (Rt.r == 15 || Rt.r == 13 || Rt2.r == 15 || Rt2.r == 13 ||
831 (S == 0 && m.r == 31) || (L == 1 && Rt.r == Rt2.r)) {
832 args << " (UNPREDICTABLE)";
833 }
834 }
Dave Allison70202782013-10-22 17:52:19 -0700835 } else if ((op3 >> 4) == 2 && op4 == 0) { // 10xxxx, op = 0
836 // fp data processing
Vladimir Markoc777e0d2014-04-03 17:59:02 +0100837 // VMLA, VMLS, VMUL, VNMUL, VADD, VSUB, VDIV, VMOV, ...
838 // |1111|1100|0|0|00|0000|1111|110|0|0|0|0|0|0000|
839 // |5 2|1 8|7|6|54|3 0|5 2|1 9|8|7|6|5|4|3 0|
840 // |----|----|-|-|--|----|----|---|-|-|-|-|-|----|
841 // |3322|2222|2|2|22|1111|1111|110|0|0|0|0|0|0000|
842 // |1 8|7 4|3|2|10|9 6|5 2|1 9|8|7|6|5|4|3 0|
843 // |----|----|-|-|--|----|----|---|-|-|-|-|-|----|
844 // |1110|1110| op3 | Vn | Vd |101|S|N|Q|M|0| Vm |
845 // |1110|1110|0|D|00| Vn | Vd |101|S|N|0|M|0| Vm | VMLA
846 // |1110|1110|0|D|00| Vn | Vd |101|S|N|1|M|0| Vm | VMLS
847 // |1110|1110|0|D|10| Vn | Vd |101|S|N|0|M|0| Vm | VMUL
848 // |1110|1110|0|D|10| Vn | Vd |101|S|N|1|M|0| Vm | VNMUL
849 // |1110|1110|0|D|11| Vn | Vd |101|S|N|0|M|0| Vm | VADD
850 // |1110|1110|0|D|11| Vn | Vd |101|S|N|1|M|0| Vm | VSUB
851 // |1110|1110|1|D|00| Vn | Vd |101|S|N|0|M|0| Vm | VDIV
852 // |1110|1110|1|D|11| iH | Vd |101|S|0|0|0|0| iL | VMOV (imm)
853 // |1110|1110|1|D|11|op5 | Vd |101|S|.|1|M|0| Vm | ... (see below)
854 uint32_t S = (instr >> 8) & 1;
855 uint32_t Q = (instr >> 6) & 1;
856 FpRegister d(instr, 12, 22);
857 FpRegister n(instr, 16, 7);
858 FpRegister m(instr, 0, 5);
Zheng Xue19649a2014-02-27 13:30:55 +0000859 if ((op3 & 0xB) == 0) { // 100x00
Vladimir Markoc777e0d2014-04-03 17:59:02 +0100860 opcode << (Q == 0 ? "vmla" : "vmls") << (S != 0 ? ".f64" : ".f32");
Zheng Xue19649a2014-02-27 13:30:55 +0000861 args << d << ", " << n << ", " << m;
Vladimir Markoc777e0d2014-04-03 17:59:02 +0100862 } else if ((op3 & 0xB) == 0x2) { // 100x10
863 opcode << (Q == 0 ? "vmul" : "vnmul") << (S != 0 ? ".f64" : ".f32");
864 args << d << ", " << n << ", " << m;
865 } else if ((op3 & 0xB) == 0x3) { // 100x11
866 opcode << (Q == 0 ? "vadd" : "vsub") << (S != 0 ? ".f64" : ".f32");
867 args << d << ", " << n << ", " << m;
868 } else if ((op3 & 0xB) == 0x8 && Q == 0) { // 101x00, Q == 0
869 opcode << "vdiv" << (S != 0 ? ".f64" : ".f32");
870 args << d << ", " << n << ", " << m;
871 } else if ((op3 & 0xB) == 0xB && Q == 0) { // 101x11, Q == 0
872 uint32_t imm8 = ((instr & 0xf0000u) >> 12) | (instr & 0xfu);
873 opcode << "vmov" << (S != 0 ? ".f64" : ".f32");
874 args << d << ", " << (S != 0 ? StringPrintf("0x%016" PRIx64, VFPExpand64(imm8))
875 : StringPrintf("0x%08x", VFPExpand32(imm8)));
876 if ((instr & 0xa0) != 0) {
877 args << " (UNPREDICTABLE)";
878 }
879 } else if ((op3 & 0xB) == 0xB && Q == 1) { // 101x11, Q == 1
880 // VNEG, VSQRT, VCMP, VCMPE, VCVT (floating-point conversion)
881 // |1111|1100|0|0|00|0000|1111|110|0|0 |0|0|0|0000|
882 // |5 2|1 8|7|6|54|3 0|5 2|1 9|8|7 |6|5|4|3 0|
883 // |----|----|-|-|--|----|----|---|-|- |-|-|-|----|
884 // |3322|2222|2|2|22|1111|1111|110|0|0 |0|0|0|0000|
885 // |1 8|7 4|3|2|10|9 6|5 2|1 9|8|7 |6|5|4|3 0|
886 // |----|----|-|-|--|----|----|---|-|- |-|-|-|----|
887 // |1110|1110|1|D|11|0000| Vd |101|S|0 |1|M|0| Vm | VMOV (reg)
888 // |1110|1110|1|D|11|0000| Vd |101|S|1 |1|M|0| Vm | VABS
889 // |1110|1110|1|D|11|0001| Vd |101|S|0 |1|M|0| Vm | VNEG
890 // |1110|1110|1|D|11|0001| Vd |101|S|1 |1|M|0| Vm | VSQRT
891 // |1110|1110|1|D|11|0100| Vd |101|S|op|1|M|0| Vm | VCMP
892 // |1110|1110|1|D|11|0101| Vd |101|S|op|1|0|0|0000| VCMPE
893 // |1110|1110|1|D|11|op5 | Vd |101|S|op|1|M|0| Vm | VCVT
894 uint32_t op5 = (instr >> 16) & 0xF;
895 uint32_t op = (instr >> 7) & 1;
896 // Register types in VCVT instructions rely on the combination of op5 and S.
897 FpRegister Dd(instr, 12, 22, 1);
898 FpRegister Sd(instr, 12, 22, 0);
899 FpRegister Dm(instr, 0, 5, 1);
900 FpRegister Sm(instr, 0, 5, 0);
901 if (op5 == 0) {
902 opcode << (op == 0 ? "vmov" : "vabs") << (S != 0 ? ".f64" : ".f32");
903 args << d << ", " << m;
904 } else if (op5 == 1) {
905 opcode << (op != 0 ? "vsqrt" : "vneg") << (S != 0 ? ".f64" : ".f32");
906 args << d << ", " << m;
907 } else if (op5 == 4) {
908 opcode << "vcmp" << (S != 0 ? ".f64" : ".f32");
909 args << d << ", " << m;
910 if (op != 0) {
911 args << " (quiet nan)";
912 }
913 } else if (op5 == 5) {
914 opcode << "vcmpe" << (S != 0 ? ".f64" : ".f32");
915 args << d << ", #0.0";
916 if (op != 0) {
917 args << " (quiet nan)";
918 }
919 if ((instr & 0x2f) != 0) {
920 args << " (UNPREDICTABLE)";
921 }
922 } else if (op5 == 0xD) {
923 if (S == 1) {
924 // vcvt{r}.s32.f64
925 opcode << "vcvt" << (op == 0 ? "r" : "") << ".s32.f64";
926 args << Sd << ", " << Dm;
927 } else {
928 // vcvt{r}.s32.f32
929 opcode << "vcvt" << (op == 0 ? "r" : "") << ".s32.f32";
930 args << Sd << ", " << Sm;
931 }
932 } else if (op5 == 0xC) {
933 if (S == 1) {
934 // vcvt{r}.u32.f64
935 opcode << "vcvt" << (op == 0 ? "r" : "") << ".u32.f64";
936 args << Sd << ", " << Dm;
937 } else {
938 // vcvt{r}.u32.f32
939 opcode << "vcvt" << (op == 0 ? "r" : "") << ".u32.f32";
940 args << Sd << ", " << Sm;
941 }
942 } else if (op5 == 0x8) {
943 if (S == 1) {
944 // vcvt.f64.<Tm>
945 opcode << "vcvt.f64." << (op == 0 ? "u" : "s") << "32";
946 args << Dd << ", " << Sm;
947 } else {
948 // vcvt.f32.<Tm>
949 opcode << "vcvt.f32." << (op == 0 ? "u" : "s") << "32";
950 args << Sd << ", " << Sm;
951 }
952 } else if (op5 == 0x7) {
953 if (op == 1) {
Zheng Xue19649a2014-02-27 13:30:55 +0000954 if (S == 1) {
Vladimir Markoc777e0d2014-04-03 17:59:02 +0100955 // vcvt.f64.f32
956 opcode << "vcvt.f64.f32";
Zheng Xue19649a2014-02-27 13:30:55 +0000957 args << Dd << ", " << Sm;
958 } else {
Vladimir Markoc777e0d2014-04-03 17:59:02 +0100959 // vcvt.f32.f64
960 opcode << "vcvt.f32.f64";
961 args << Sd << ", " << Dm;
Zheng Xue19649a2014-02-27 13:30:55 +0000962 }
963 }
Vladimir Markoc777e0d2014-04-03 17:59:02 +0100964 } else if ((op5 & 0xa) == 0xa) {
965 opcode << "vcvt";
966 args << "[undecoded: floating <-> fixed]";
Zheng Xue19649a2014-02-27 13:30:55 +0000967 }
968 }
Dave Allison70202782013-10-22 17:52:19 -0700969 } else if ((op3 >> 4) == 2 && op4 == 1) { // 10xxxx, op = 1
Vladimir Markodd577a32013-11-07 19:25:24 +0000970 if (coproc == 10 && (op3 & 0xE) == 0) {
971 // VMOV (between ARM core register and single-precision register)
972 // |1111|1100|000|0 |0000|1111|1100|0|00|0|0000|
973 // |5 |1 8|7 5|4 |3 0|5 2|1 8|7|65|4|3 0|
974 // |----|----|---|- |----|----|----|-|--|-|----|
975 // |3322|2222|222|2 |1111|1111|1100|0|00|0|0000|
976 // |1 8|7 4|3 1|0 |9 6|5 2|1 8|7|65|4|3 0|
977 // |----|----|---|- |----|----|----|-|--|-|----|
978 // |1110|1110|000|op| Vn | Rt |1010|N|00|1|0000|
979 uint32_t op = op3 & 1;
980 ArmRegister Rt(instr, 12);
981 FpRegister n(instr, 16, 7);
982 opcode << "vmov.f32";
983 if (op) {
984 args << Rt << ", " << n;
985 } else {
986 args << n << ", " << Rt;
987 }
988 if (Rt.r == 13 || Rt.r == 15 || (instr & 0x6F) != 0) {
989 args << " (UNPREDICTABLE)";
990 }
991 } else if (coproc == 10 && op3 == 0x2F) {
992 // VMRS
993 // |1111|11000000|0000|1111|1100|000|0|0000|
994 // |5 |1 4|3 0|5 2|1 8|7 5|4|3 0|
995 // |----|--------|----|----|----|---|-|----|
996 // |3322|22222222|1111|1111|1100|000|0|0000|
997 // |1 8|7 0|9 6|5 2|1 8|7 5|4|3 0|
998 // |----|--------|----|----|----|---|-|----|
999 // |1110|11101111|reg | Rt |1010|000|1|0000| - last 7 0s are (0)
1000 uint32_t spec_reg = (instr >> 16) & 0xF;
1001 ArmRegister Rt(instr, 12);
1002 opcode << "vmrs";
1003 if (spec_reg == 1) {
1004 if (Rt.r == 15) {
1005 args << "APSR_nzcv, FPSCR";
1006 } else if (Rt.r == 13) {
1007 args << Rt << ", FPSCR (UNPREDICTABLE)";
1008 } else {
1009 args << Rt << ", FPSCR";
1010 }
1011 } else {
1012 args << "(PRIVILEGED)";
1013 }
1014 } else if (coproc == 11 && (op3 & 0x9) != 8) {
1015 // VMOV (ARM core register to scalar or vice versa; 8/16/32-bit)
1016 }
Ian Rogers9af89402012-09-07 11:29:35 -07001017 }
Dave Allison70202782013-10-22 17:52:19 -07001018 }
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001019 }
1020 break;
Ian Rogers40627db2012-03-04 17:31:09 -08001021 case 2:
1022 if ((instr & 0x8000) == 0 && (op2 & 0x20) == 0) {
1023 // Data-processing (modified immediate)
1024 // |111|11|10|0000|0|0000|1|111|1100|00000000|
1025 // |5 3|21|09|8765|4|3 0|5|4 2|10 8|7 5 0|
1026 // |---|--|--|----|-|----|-|---|----|--------|
1027 // |332|22|22|2222|2|1111|1|111|1100|00000000|
1028 // |1 9|87|65|4321|0|9 6|5|4 2|10 8|7 5 0|
1029 // |---|--|--|----|-|----|-|---|----|--------|
1030 // |111|10|i0| op3|S| Rn |0|iii| Rd |iiiiiiii|
1031 // 111 10 x0 xxxx x xxxx opxxx xxxx xxxxxxxx
Ian Rogers40627db2012-03-04 17:31:09 -08001032 uint32_t i = (instr >> 26) & 1;
1033 uint32_t op3 = (instr >> 21) & 0xF;
1034 uint32_t S = (instr >> 20) & 1;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001035 ArmRegister Rn(instr, 16);
Ian Rogers40627db2012-03-04 17:31:09 -08001036 uint32_t imm3 = (instr >> 12) & 7;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001037 ArmRegister Rd(instr, 8);
Ian Rogers40627db2012-03-04 17:31:09 -08001038 uint32_t imm8 = instr & 0xFF;
Jeff Hao7cb0f9c2013-02-04 16:15:27 -08001039 int32_t imm32 = (i << 11) | (imm3 << 8) | imm8;
1040 if (Rn.r == 0xF && (op3 == 0x2 || op3 == 0x3)) {
1041 if (op3 == 0x2) {
1042 opcode << "mov";
1043 if (S == 1) {
1044 opcode << "s";
1045 }
1046 opcode << ".w";
1047 } else {
1048 opcode << "mvn";
1049 if (S == 1) {
1050 opcode << "s";
1051 }
1052 }
Ian Rogersa9650dd2013-10-04 08:23:32 -07001053 args << Rd << ", #" << ThumbExpand(imm32);
Jeff Hao7cb0f9c2013-02-04 16:15:27 -08001054 } else if (Rd.r == 0xF && S == 1 &&
1055 (op3 == 0x0 || op3 == 0x4 || op3 == 0x8 || op3 == 0xD)) {
1056 if (op3 == 0x0) {
1057 opcode << "tst";
1058 } else if (op3 == 0x4) {
1059 opcode << "teq";
1060 } else if (op3 == 0x8) {
Vladimir Marko22479842013-11-19 17:04:50 +00001061 opcode << "cmn.w";
Jeff Hao7cb0f9c2013-02-04 16:15:27 -08001062 } else {
1063 opcode << "cmp.w";
1064 }
Ian Rogersa9650dd2013-10-04 08:23:32 -07001065 args << Rn << ", #" << ThumbExpand(imm32);
Jeff Hao7cb0f9c2013-02-04 16:15:27 -08001066 } else {
1067 switch (op3) {
1068 case 0x0: opcode << "and"; break;
1069 case 0x1: opcode << "bic"; break;
1070 case 0x2: opcode << "orr"; break;
1071 case 0x3: opcode << "orn"; break;
1072 case 0x4: opcode << "eor"; break;
1073 case 0x8: opcode << "add"; break;
1074 case 0xA: opcode << "adc"; break;
1075 case 0xB: opcode << "sbc"; break;
1076 case 0xD: opcode << "sub"; break;
1077 case 0xE: opcode << "rsb"; break;
1078 default: opcode << "UNKNOWN DPMI-" << op3; break;
1079 }
1080 if (S == 1) {
1081 opcode << "s";
1082 }
Ian Rogersa9650dd2013-10-04 08:23:32 -07001083 args << Rd << ", " << Rn << ", #" << ThumbExpand(imm32);
Ian Rogers40627db2012-03-04 17:31:09 -08001084 }
Ian Rogers40627db2012-03-04 17:31:09 -08001085 } else if ((instr & 0x8000) == 0 && (op2 & 0x20) != 0) {
1086 // Data-processing (plain binary immediate)
1087 // |111|11|10|00000|0000|1|111110000000000|
1088 // |5 3|21|09|87654|3 0|5|4 0 5 0|
1089 // |---|--|--|-----|----|-|---------------|
1090 // |332|22|22|22222|1111|1|111110000000000|
1091 // |1 9|87|65|43210|9 6|5|4 0 5 0|
1092 // |---|--|--|-----|----|-|---------------|
1093 // |111|10|x1| op3 | Rn |0|xxxxxxxxxxxxxxx|
1094 uint32_t op3 = (instr >> 20) & 0x1F;
Ian Rogers40627db2012-03-04 17:31:09 -08001095 switch (op3) {
Ian Rogers55019132013-02-08 01:05:23 -08001096 case 0x00: case 0x0A: {
1097 // ADD/SUB.W Rd, Rn #imm12 - 111 10 i1 0101 0 nnnn 0 iii dddd iiiiiiii
Ian Rogers66a3fca2012-04-09 19:51:34 -07001098 ArmRegister Rd(instr, 8);
1099 ArmRegister Rn(instr, 16);
1100 uint32_t i = (instr >> 26) & 1;
1101 uint32_t imm3 = (instr >> 12) & 0x7;
1102 uint32_t imm8 = instr & 0xFF;
1103 uint32_t imm12 = (i << 11) | (imm3 << 8) | imm8;
1104 if (Rn.r != 0xF) {
Ian Rogers55019132013-02-08 01:05:23 -08001105 opcode << (op3 == 0 ? "addw" : "subw");
Ian Rogers66a3fca2012-04-09 19:51:34 -07001106 args << Rd << ", " << Rn << ", #" << imm12;
1107 } else {
1108 opcode << "adr";
1109 args << Rd << ", ";
Ian Rogers55019132013-02-08 01:05:23 -08001110 DumpBranchTarget(args, instr_ptr + 4, (op3 == 0) ? imm12 : -imm12);
Ian Rogers66a3fca2012-04-09 19:51:34 -07001111 }
1112 break;
1113 }
Ian Rogers55019132013-02-08 01:05:23 -08001114 case 0x04: case 0x0C: {
1115 // MOVW/T Rd, #imm16 - 111 10 i0 0010 0 iiii 0 iii dddd iiiiiiii
Elliott Hughes630e77d2012-03-22 19:20:56 -07001116 ArmRegister Rd(instr, 8);
Ian Rogers40627db2012-03-04 17:31:09 -08001117 uint32_t i = (instr >> 26) & 1;
1118 uint32_t imm3 = (instr >> 12) & 0x7;
1119 uint32_t imm8 = instr & 0xFF;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001120 uint32_t Rn = (instr >> 16) & 0xF;
Ian Rogers40627db2012-03-04 17:31:09 -08001121 uint32_t imm16 = (Rn << 12) | (i << 11) | (imm3 << 8) | imm8;
Ian Rogers55019132013-02-08 01:05:23 -08001122 opcode << (op3 == 0x04 ? "movw" : "movt");
Elliott Hughes630e77d2012-03-22 19:20:56 -07001123 args << Rd << ", #" << imm16;
Ian Rogers40627db2012-03-04 17:31:09 -08001124 break;
1125 }
jeffhaoeae26912013-01-28 16:29:54 -08001126 case 0x16: {
1127 // BFI Rd, Rn, #lsb, #width - 111 10 0 11 011 0 nnnn 0 iii dddd ii 0 iiiii
1128 ArmRegister Rd(instr, 8);
1129 ArmRegister Rn(instr, 16);
1130 uint32_t msb = instr & 0x1F;
1131 uint32_t imm2 = (instr >> 6) & 0x3;
1132 uint32_t imm3 = (instr >> 12) & 0x7;
1133 uint32_t lsb = (imm3 << 2) | imm2;
1134 uint32_t width = msb - lsb + 1;
1135 if (Rn.r != 0xF) {
1136 opcode << "bfi";
1137 args << Rd << ", " << Rn << ", #" << lsb << ", #" << width;
1138 } else {
1139 opcode << "bfc";
1140 args << Rd << ", #" << lsb << ", #" << width;
1141 }
1142 break;
1143 }
Ian Rogers40627db2012-03-04 17:31:09 -08001144 default:
1145 break;
1146 }
1147 } else {
1148 // Branches and miscellaneous control
1149 // |111|11|1000000|0000|1|111|1100|00000000|
1150 // |5 3|21|0987654|3 0|5|4 2|10 8|7 5 0|
1151 // |---|--|-------|----|-|---|----|--------|
1152 // |332|22|2222222|1111|1|111|1100|00000000|
1153 // |1 9|87|6543210|9 6|5|4 2|10 8|7 5 0|
1154 // |---|--|-------|----|-|---|----|--------|
1155 // |111|10| op2 | |1|op3|op4 | |
1156
1157 uint32_t op3 = (instr >> 12) & 7;
Brian Carlstrom7934ac22013-07-26 10:54:15 -07001158 // uint32_t op4 = (instr >> 8) & 0xF;
Ian Rogers40627db2012-03-04 17:31:09 -08001159 switch (op3) {
1160 case 0:
1161 if ((op2 & 0x38) != 0x38) {
1162 // Conditional branch
1163 // |111|11|1|0000|000000|1|1|1 |1|1 |10000000000|
1164 // |5 3|21|0|9876|543 0|5|4|3 |2|1 |0 5 0|
1165 // |---|--|-|----|------|-|-|--|-|--|-----------|
1166 // |332|22|2|2222|221111|1|1|1 |1|1 |10000000000|
1167 // |1 9|87|6|5432|109 6|5|4|3 |2|1 |0 5 0|
1168 // |---|--|-|----|------|-|-|--|-|--|-----------|
1169 // |111|10|S|cond| imm6 |1|0|J1|0|J2| imm11 |
1170 uint32_t S = (instr >> 26) & 1;
1171 uint32_t J2 = (instr >> 11) & 1;
1172 uint32_t J1 = (instr >> 13) & 1;
1173 uint32_t imm6 = (instr >> 16) & 0x3F;
1174 uint32_t imm11 = instr & 0x7FF;
1175 uint32_t cond = (instr >> 22) & 0xF;
1176 int32_t imm32 = (S << 20) | (J2 << 19) | (J1 << 18) | (imm6 << 12) | (imm11 << 1);
1177 imm32 = (imm32 << 11) >> 11; // sign extend 21bit immediate
Elliott Hughescbf0b612012-03-15 16:23:47 -07001178 opcode << "b";
1179 DumpCond(opcode, cond);
1180 opcode << ".w";
1181 DumpBranchTarget(args, instr_ptr + 4, imm32);
Ian Rogers9af89402012-09-07 11:29:35 -07001182 } else if (op2 == 0x3B) {
1183 // Miscellaneous control instructions
1184 uint32_t op5 = (instr >> 4) & 0xF;
1185 switch (op5) {
Ian Rogersb122a4b2013-11-19 18:00:50 -08001186 case 4: opcode << "dsb"; DumpMemoryDomain(args, instr & 0xF); break;
1187 case 5: opcode << "dmb"; DumpMemoryDomain(args, instr & 0xF); break;
1188 case 6: opcode << "isb"; DumpMemoryDomain(args, instr & 0xF); break;
Ian Rogers9af89402012-09-07 11:29:35 -07001189 }
Ian Rogers40627db2012-03-04 17:31:09 -08001190 }
1191 break;
1192 case 2:
Ian Rogersd0876a92013-02-08 11:30:38 -08001193 if ((op2 & 0x38) == 0x38) {
1194 if (op2 == 0x7F) {
1195 opcode << "udf";
1196 }
1197 break;
1198 }
1199 // Else deliberate fall-through to B.
1200 case 1: case 3: {
1201 // B
1202 // |111|11|1|0000|000000|11|1 |1|1 |10000000000|
1203 // |5 3|21|0|9876|543 0|54|3 |2|1 |0 5 0|
1204 // |---|--|-|----|------|--|--|-|--|-----------|
1205 // |332|22|2|2222|221111|11|1 |1|1 |10000000000|
1206 // |1 9|87|6|5 2|10 6|54|3 |2|1 |0 5 0|
1207 // |---|--|-|----|------|--|--|-|--|-----------|
1208 // |111|10|S|cond| imm6 |10|J1|0|J2| imm11 |
1209 // |111|10|S| imm10 |10|J1|1|J2| imm11 |
1210 uint32_t S = (instr >> 26) & 1;
1211 uint32_t cond = (instr >> 22) & 0xF;
1212 uint32_t J2 = (instr >> 11) & 1;
1213 uint32_t form = (instr >> 12) & 1;
1214 uint32_t J1 = (instr >> 13) & 1;
1215 uint32_t imm10 = (instr >> 16) & 0x3FF;
1216 uint32_t imm6 = (instr >> 16) & 0x3F;
1217 uint32_t imm11 = instr & 0x7FF;
1218 opcode << "b";
1219 int32_t imm32;
1220 if (form == 0) {
1221 DumpCond(opcode, cond);
1222 imm32 = (S << 20) | (J2 << 19) | (J1 << 18) | (imm6 << 12) | (imm11 << 1);
1223 imm32 = (imm32 << 11) >> 11; // sign extend 21 bit immediate.
1224 } else {
1225 uint32_t I1 = ~(J1 ^ S);
1226 uint32_t I2 = ~(J2 ^ S);
1227 imm32 = (S << 24) | (I1 << 23) | (I2 << 22) | (imm10 << 12) | (imm11 << 1);
1228 imm32 = (imm32 << 8) >> 8; // sign extend 24 bit immediate.
1229 }
1230 opcode << ".w";
1231 DumpBranchTarget(args, instr_ptr + 4, imm32);
Ian Rogers40627db2012-03-04 17:31:09 -08001232 break;
Ian Rogersd0876a92013-02-08 11:30:38 -08001233 }
Ian Rogers40627db2012-03-04 17:31:09 -08001234 case 4: case 6: case 5: case 7: {
1235 // BL, BLX (immediate)
1236 // |111|11|1|0000000000|11|1 |1|1 |10000000000|
1237 // |5 3|21|0|9876543 0|54|3 |2|1 |0 5 0|
1238 // |---|--|-|----------|--|--|-|--|-----------|
1239 // |332|22|2|2222221111|11|1 |1|1 |10000000000|
1240 // |1 9|87|6|5 0 6|54|3 |2|1 |0 5 0|
1241 // |---|--|-|----------|--|--|-|--|-----------|
Dave Allisond6ed6422014-04-09 23:36:15 +00001242 // |111|10|S| imm10 |11|J1|L|J2| imm11 |
Ian Rogers40627db2012-03-04 17:31:09 -08001243 uint32_t S = (instr >> 26) & 1;
1244 uint32_t J2 = (instr >> 11) & 1;
Dave Allisond6ed6422014-04-09 23:36:15 +00001245 uint32_t L = (instr >> 12) & 1;
Ian Rogers40627db2012-03-04 17:31:09 -08001246 uint32_t J1 = (instr >> 13) & 1;
1247 uint32_t imm10 = (instr >> 16) & 0x3FF;
1248 uint32_t imm11 = instr & 0x7FF;
Dave Allisond6ed6422014-04-09 23:36:15 +00001249 if (L == 0) {
1250 opcode << "bx";
Dave Allisonf9487c02014-04-08 23:08:12 +00001251 } else {
Dave Allisond6ed6422014-04-09 23:36:15 +00001252 opcode << "blx";
Ian Rogers40627db2012-03-04 17:31:09 -08001253 }
1254 uint32_t I1 = ~(J1 ^ S);
1255 uint32_t I2 = ~(J2 ^ S);
1256 int32_t imm32 = (S << 24) | (I1 << 23) | (I2 << 22) | (imm10 << 12) | (imm11 << 1);
1257 imm32 = (imm32 << 8) >> 8; // sign extend 24 bit immediate.
Elliott Hughescbf0b612012-03-15 16:23:47 -07001258 DumpBranchTarget(args, instr_ptr + 4, imm32);
Ian Rogers40627db2012-03-04 17:31:09 -08001259 break;
1260 }
1261 }
1262 }
1263 break;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001264 case 3:
1265 switch (op2) {
1266 case 0x00: case 0x02: case 0x04: case 0x06: // 000xxx0
Nicolas Geoffray3c7bb982014-07-23 16:04:16 +01001267 case 0x08: case 0x09: case 0x0A: case 0x0C: case 0x0E: {
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001268 // Store single data item
Ian Rogers40627db2012-03-04 17:31:09 -08001269 // |111|11|100|000|0|0000|1111|110000|000000|
1270 // |5 3|21|098|765|4|3 0|5 2|10 6|5 0|
1271 // |---|--|---|---|-|----|----|------|------|
1272 // |332|22|222|222|2|1111|1111|110000|000000|
1273 // |1 9|87|654|321|0|9 6|5 2|10 6|5 0|
1274 // |---|--|---|---|-|----|----|------|------|
1275 // |111|11|000|op3|0| | | op4 | |
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001276 uint32_t op3 = (instr >> 21) & 7;
Brian Carlstrom7934ac22013-07-26 10:54:15 -07001277 // uint32_t op4 = (instr >> 6) & 0x3F;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001278 switch (op3) {
Ian Rogers087b2412012-03-21 01:30:32 -07001279 case 0x0: case 0x4: {
Nicolas Geoffray3c7bb982014-07-23 16:04:16 +01001280 // {ST,LD}RB Rt,[Rn,#+/-imm12] - 111 11 00 0 1 00 0 nnnn tttt 1 PUWii ii iiii
1281 // {ST,LD}RB Rt,[Rn,#+/-imm8] - 111 11 00 0 0 00 0 nnnn tttt 1 PUWii ii iiii
1282 // {ST,LD}RB Rt,[Rn,Rm,lsl #imm2] - 111 11 00 0 0 00 0 nnnn tttt 0 00000 ii mmmm
Elliott Hughes630e77d2012-03-22 19:20:56 -07001283 ArmRegister Rn(instr, 16);
1284 ArmRegister Rt(instr, 12);
Nicolas Geoffray3c7bb982014-07-23 16:04:16 +01001285 opcode << (HasBitSet(instr, 20) ? "ldrb" : "strb");
1286 if (HasBitSet(instr, 23)) {
1287 uint32_t imm12 = instr & 0xFFF;
1288 args << Rt << ", [" << Rn << ",#" << imm12 << "]";
1289 } else if ((instr & 0x800) != 0) {
1290 uint32_t imm8 = instr & 0xFF;
1291 args << Rt << ", [" << Rn << ",#" << imm8 << "]";
1292 } else {
1293 uint32_t imm2 = (instr >> 4) & 3;
1294 ArmRegister Rm(instr, 0);
1295 args << Rt << ", [" << Rn << ", " << Rm;
1296 if (imm2 != 0) {
1297 args << ", " << "lsl #" << imm2;
1298 }
1299 args << "]";
1300 }
1301 break;
1302 }
1303 case 0x1: case 0x5: {
1304 // STRH Rt,[Rn,#+/-imm12] - 111 11 00 0 1 01 0 nnnn tttt 1 PUWii ii iiii
1305 // STRH Rt,[Rn,#+/-imm8] - 111 11 00 0 0 01 0 nnnn tttt 1 PUWii ii iiii
1306 // STRH Rt,[Rn,Rm,lsl #imm2] - 111 11 00 0 0 01 0 nnnn tttt 0 00000 ii mmmm
1307 ArmRegister Rn(instr, 16);
1308 ArmRegister Rt(instr, 12);
1309 opcode << "strh";
1310 if (HasBitSet(instr, 23)) {
1311 uint32_t imm12 = instr & 0xFFF;
1312 args << Rt << ", [" << Rn << ",#" << imm12 << "]";
1313 } else if ((instr & 0x800) != 0) {
Ian Rogers087b2412012-03-21 01:30:32 -07001314 uint32_t imm8 = instr & 0xFF;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001315 args << Rt << ", [" << Rn << ",#" << imm8 << "]";
Ian Rogers087b2412012-03-21 01:30:32 -07001316 } else {
1317 uint32_t imm2 = (instr >> 4) & 3;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001318 ArmRegister Rm(instr, 0);
1319 args << Rt << ", [" << Rn << ", " << Rm;
Ian Rogers087b2412012-03-21 01:30:32 -07001320 if (imm2 != 0) {
1321 args << ", " << "lsl #" << imm2;
1322 }
1323 args << "]";
1324 }
1325 break;
1326 }
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001327 case 0x2: case 0x6: {
Elliott Hughes630e77d2012-03-22 19:20:56 -07001328 ArmRegister Rn(instr, 16);
1329 ArmRegister Rt(instr, 12);
Ian Rogers40627db2012-03-04 17:31:09 -08001330 if (op3 == 2) {
Ian Rogers66a3fca2012-04-09 19:51:34 -07001331 if ((instr & 0x800) != 0) {
1332 // STR Rt, [Rn, #imm8] - 111 11 000 010 0 nnnn tttt 1PUWiiiiiiii
1333 uint32_t P = (instr >> 10) & 1;
1334 uint32_t U = (instr >> 9) & 1;
1335 uint32_t W = (instr >> 8) & 1;
1336 uint32_t imm8 = instr & 0xFF;
1337 int32_t imm32 = (imm8 << 24) >> 24; // sign-extend imm8
1338 if (Rn.r == 13 && P == 1 && U == 0 && W == 1 && imm32 == 4) {
1339 opcode << "push";
Dave Allison20dfc792014-06-16 20:44:29 -07001340 args << "{" << Rt << "}";
Ian Rogers66a3fca2012-04-09 19:51:34 -07001341 } else if (Rn.r == 15 || (P == 0 && W == 0)) {
1342 opcode << "UNDEFINED";
Ian Rogers40627db2012-03-04 17:31:09 -08001343 } else {
Ian Rogers66a3fca2012-04-09 19:51:34 -07001344 if (P == 1 && U == 1 && W == 0) {
1345 opcode << "strt";
1346 } else {
1347 opcode << "str";
1348 }
1349 args << Rt << ", [" << Rn;
1350 if (P == 0 && W == 1) {
1351 args << "], #" << imm32;
1352 } else {
1353 args << ", #" << imm32 << "]";
1354 if (W == 1) {
1355 args << "!";
1356 }
Ian Rogers40627db2012-03-04 17:31:09 -08001357 }
1358 }
Ian Rogers66a3fca2012-04-09 19:51:34 -07001359 } else {
1360 // STR Rt, [Rn, Rm, LSL #imm2] - 111 11 000 010 0 nnnn tttt 000000iimmmm
1361 ArmRegister Rn(instr, 16);
1362 ArmRegister Rt(instr, 12);
1363 ArmRegister Rm(instr, 0);
1364 uint32_t imm2 = (instr >> 4) & 3;
1365 opcode << "str.w";
1366 args << Rt << ", [" << Rn << ", " << Rm;
1367 if (imm2 != 0) {
1368 args << ", lsl #" << imm2;
1369 }
1370 args << "]";
Ian Rogers40627db2012-03-04 17:31:09 -08001371 }
1372 } else if (op3 == 6) {
Ian Rogers66a3fca2012-04-09 19:51:34 -07001373 // STR.W Rt, [Rn, #imm12] - 111 11 000 110 0 nnnn tttt iiiiiiiiiiii
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001374 uint32_t imm12 = instr & 0xFFF;
Elliott Hughescbf0b612012-03-15 16:23:47 -07001375 opcode << "str.w";
Elliott Hughes630e77d2012-03-22 19:20:56 -07001376 args << Rt << ", [" << Rn << ", #" << imm12 << "]";
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001377 }
Ian Rogers40627db2012-03-04 17:31:09 -08001378 break;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001379 }
1380 }
1381
1382 break;
1383 }
Nicolas Geoffray3c7bb982014-07-23 16:04:16 +01001384 case 0x03: case 0x0B: case 0x11: case 0x13: case 0x19: case 0x1B: { // 00xx011
1385 // Load byte/halfword
jeffhaoeae26912013-01-28 16:29:54 -08001386 // |111|11|10|0 0|00|0|0000|1111|110000|000000|
1387 // |5 3|21|09|8 7|65|4|3 0|5 2|10 6|5 0|
1388 // |---|--|--|---|--|-|----|----|------|------|
1389 // |332|22|22|2 2|22|2|1111|1111|110000|000000|
1390 // |1 9|87|65|4 3|21|0|9 6|5 2|10 6|5 0|
1391 // |---|--|--|---|--|-|----|----|------|------|
1392 // |111|11|00|op3|01|1| Rn | Rt | op4 | |
1393 // |111|11| op2 | | | imm12 |
1394 uint32_t op3 = (instr >> 23) & 3;
1395 ArmRegister Rn(instr, 16);
1396 ArmRegister Rt(instr, 12);
1397 if (Rt.r != 15) {
1398 if (op3 == 1) {
1399 // LDRH.W Rt, [Rn, #imm12] - 111 11 00 01 011 nnnn tttt iiiiiiiiiiii
1400 uint32_t imm12 = instr & 0xFFF;
1401 opcode << "ldrh.w";
1402 args << Rt << ", [" << Rn << ", #" << imm12 << "]";
1403 if (Rn.r == 9) {
1404 args << " ; ";
Ian Rogersdd7624d2014-03-14 17:43:00 -07001405 Thread::DumpThreadOffset<4>(args, imm12);
jeffhaoeae26912013-01-28 16:29:54 -08001406 } else if (Rn.r == 15) {
1407 intptr_t lit_adr = reinterpret_cast<intptr_t>(instr_ptr);
1408 lit_adr = RoundDown(lit_adr, 4) + 4 + imm12;
Ian Rogersff093b32014-04-30 19:04:27 -07001409 args << StringPrintf(" ; 0x%08x", *reinterpret_cast<int32_t*>(lit_adr));
jeffhaoeae26912013-01-28 16:29:54 -08001410 }
1411 } else if (op3 == 3) {
1412 // LDRSH.W Rt, [Rn, #imm12] - 111 11 00 11 011 nnnn tttt iiiiiiiiiiii
Nicolas Geoffray3c7bb982014-07-23 16:04:16 +01001413 // LDRSB.W Rt, [Rn, #imm12] - 111 11 00 11 001 nnnn tttt iiiiiiiiiiii
jeffhaoeae26912013-01-28 16:29:54 -08001414 uint32_t imm12 = instr & 0xFFF;
Nicolas Geoffray3c7bb982014-07-23 16:04:16 +01001415 opcode << (HasBitSet(instr, 20) ? "ldrsb.w" : "ldrsh.w");
jeffhaoeae26912013-01-28 16:29:54 -08001416 args << Rt << ", [" << Rn << ", #" << imm12 << "]";
1417 if (Rn.r == 9) {
1418 args << " ; ";
Ian Rogersdd7624d2014-03-14 17:43:00 -07001419 Thread::DumpThreadOffset<4>(args, imm12);
jeffhaoeae26912013-01-28 16:29:54 -08001420 } else if (Rn.r == 15) {
1421 intptr_t lit_adr = reinterpret_cast<intptr_t>(instr_ptr);
1422 lit_adr = RoundDown(lit_adr, 4) + 4 + imm12;
Ian Rogersff093b32014-04-30 19:04:27 -07001423 args << StringPrintf(" ; 0x%08x", *reinterpret_cast<int32_t*>(lit_adr));
jeffhaoeae26912013-01-28 16:29:54 -08001424 }
1425 }
1426 }
1427 break;
1428 }
Vladimir Markoa8b4caf2013-10-24 15:08:57 +01001429 case 0x29: { // 0101001
1430 // |111|11|1000000|0000|1111|1100|00|0 0|0000|
1431 // |5 3|21|0 4|3 0|5 2|1 8|76|5 4|3 0|
1432 // |---|--|-------|----|----|----|--|---|----|
1433 // |332|22|2222222|1111|1111|1100|00|0 0|0000|
1434 // |1 9|87|6 0|9 6|5 2|1 8|76|5 4|3 0|
1435 // |---|--|-------|----|----|----|--|---|----|
1436 // |111|11|0101001| Rm |1111| Rd |11|op3| Rm |
1437 // REV - 111 11 0101001 mmmm 1111 dddd 1000 mmmm
1438 // REV16 - 111 11 0101001 mmmm 1111 dddd 1001 mmmm
1439 // RBIT - 111 11 0101001 mmmm 1111 dddd 1010 mmmm
1440 // REVSH - 111 11 0101001 mmmm 1111 dddd 1011 mmmm
1441 if ((instr & 0xf0c0) == 0xf080) {
1442 uint32_t op3 = (instr >> 4) & 3;
1443 opcode << kThumbReverseOperations[op3];
1444 ArmRegister Rm(instr, 0);
1445 ArmRegister Rd(instr, 8);
1446 args << Rd << ", " << Rm;
1447 ArmRegister Rm2(instr, 16);
1448 if (Rm.r != Rm2.r || Rm.r == 13 || Rm.r == 15 || Rd.r == 13 || Rd.r == 15) {
1449 args << " (UNPREDICTABLE)";
1450 }
Vladimir Marko1f6754d2013-10-28 20:27:17 +00001451 } // else unknown instruction
Vladimir Markoa8b4caf2013-10-24 15:08:57 +01001452 break;
1453 }
Brian Carlstrom7934ac22013-07-26 10:54:15 -07001454 case 0x05: case 0x0D: case 0x15: case 0x1D: { // 00xx101
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001455 // Load word
1456 // |111|11|10|0 0|00|0|0000|1111|110000|000000|
1457 // |5 3|21|09|8 7|65|4|3 0|5 2|10 6|5 0|
1458 // |---|--|--|---|--|-|----|----|------|------|
1459 // |332|22|22|2 2|22|2|1111|1111|110000|000000|
1460 // |1 9|87|65|4 3|21|0|9 6|5 2|10 6|5 0|
1461 // |---|--|--|---|--|-|----|----|------|------|
1462 // |111|11|00|op3|10|1| Rn | Rt | op4 | |
1463 // |111|11| op2 | | | imm12 |
1464 uint32_t op3 = (instr >> 23) & 3;
1465 uint32_t op4 = (instr >> 6) & 0x3F;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001466 ArmRegister Rn(instr, 16);
1467 ArmRegister Rt(instr, 12);
1468 if (op3 == 1 || Rn.r == 15) {
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001469 // LDR.W Rt, [Rn, #imm12] - 111 11 00 00 101 nnnn tttt iiiiiiiiiiii
1470 // LDR.W Rt, [PC, #imm12] - 111 11 00 0x 101 1111 tttt iiiiiiiiiiii
1471 uint32_t imm12 = instr & 0xFFF;
Elliott Hughescbf0b612012-03-15 16:23:47 -07001472 opcode << "ldr.w";
Elliott Hughes630e77d2012-03-22 19:20:56 -07001473 args << Rt << ", [" << Rn << ", #" << imm12 << "]";
Elliott Hughes28fa76d2012-04-09 17:31:46 -07001474 if (Rn.r == 9) {
1475 args << " ; ";
Ian Rogersdd7624d2014-03-14 17:43:00 -07001476 Thread::DumpThreadOffset<4>(args, imm12);
Ian Rogers5b9b1bc2012-04-09 22:51:43 -07001477 } else if (Rn.r == 15) {
1478 intptr_t lit_adr = reinterpret_cast<intptr_t>(instr_ptr);
1479 lit_adr = RoundDown(lit_adr, 4) + 4 + imm12;
Ian Rogersff093b32014-04-30 19:04:27 -07001480 args << StringPrintf(" ; 0x%08x", *reinterpret_cast<int32_t*>(lit_adr));
Elliott Hughes28fa76d2012-04-09 17:31:46 -07001481 }
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001482 } else if (op4 == 0) {
1483 // LDR.W Rt, [Rn, Rm{, LSL #imm2}] - 111 11 00 00 101 nnnn tttt 000000iimmmm
1484 uint32_t imm2 = (instr >> 4) & 0xF;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001485 ArmRegister rm(instr, 0);
Elliott Hughescbf0b612012-03-15 16:23:47 -07001486 opcode << "ldr.w";
Elliott Hughes630e77d2012-03-22 19:20:56 -07001487 args << Rt << ", [" << Rn << ", " << rm;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001488 if (imm2 != 0) {
Elliott Hughescbf0b612012-03-15 16:23:47 -07001489 args << ", lsl #" << imm2;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001490 }
Elliott Hughescbf0b612012-03-15 16:23:47 -07001491 args << "]";
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001492 } else {
Dave Allison20dfc792014-06-16 20:44:29 -07001493 bool p = (instr & (1 << 10)) != 0;
1494 bool w = (instr & (1 << 8)) != 0;
1495 bool u = (instr & (1 << 9)) != 0;
1496 if (p && u && !w) {
1497 // LDRT Rt, [Rn, #imm8] - 111 11 00 00 101 nnnn tttt 1110iiiiiiii
1498 uint32_t imm8 = instr & 0xFF;
1499 opcode << "ldrt";
1500 args << Rt << ", [" << Rn << ", #" << imm8 << "]";
1501 } else if (Rn.r == 13 && !p && u && w && (instr & 0xff) == 4) {
1502 // POP
1503 opcode << "pop";
1504 args << "{" << Rt << "}";
1505 } else {
1506 bool wback = !p || w;
1507 uint32_t offset = (instr & 0xff);
1508 opcode << "ldr.w";
1509 args << Rt << ",";
1510 if (p && !wback) {
1511 args << "[" << Rn << ", #" << offset << "]";
1512 } else if (p && wback) {
1513 args << "[" << Rn << ", #" << offset << "]!";
1514 } else if (!p && wback) {
1515 args << "[" << Rn << "], #" << offset;
1516 } else {
1517 LOG(FATAL) << p << " " << w;
1518 }
1519 }
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001520 }
1521 break;
1522 }
Dave Allison70202782013-10-22 17:52:19 -07001523 default: // more formats
1524 if ((op2 >> 4) == 2) { // 010xxxx
1525 // data processing (register)
Vladimir Markoc777e0d2014-04-03 17:59:02 +01001526 if ((instr & 0x0080f0f0) == 0x0000f000) {
1527 // LSL, LSR, ASR, ROR
1528 uint32_t shift_op = (instr >> 21) & 3;
1529 uint32_t S = (instr >> 20) & 1;
1530 ArmRegister Rd(instr, 8);
1531 ArmRegister Rn(instr, 16);
1532 ArmRegister Rm(instr, 0);
1533 opcode << kThumb2ShiftOperations[shift_op] << (S != 0 ? "s" : "");
1534 args << Rd << ", " << Rn << ", " << Rm;
1535 }
Dave Allison70202782013-10-22 17:52:19 -07001536 } else if ((op2 >> 3) == 6) { // 0110xxx
1537 // Multiply, multiply accumulate, and absolute difference
1538 op1 = (instr >> 20) & 0x7;
1539 op2 = (instr >> 4) & 0x2;
1540 ArmRegister Ra(instr, 12);
1541 ArmRegister Rn(instr, 16);
1542 ArmRegister Rm(instr, 0);
1543 ArmRegister Rd(instr, 8);
1544 switch (op1) {
1545 case 0:
1546 if (op2 == 0) {
1547 if (Ra.r == 0xf) {
1548 opcode << "mul";
1549 args << Rd << ", " << Rn << ", " << Rm;
1550 } else {
1551 opcode << "mla";
1552 args << Rd << ", " << Rn << ", " << Rm << ", " << Ra;
1553 }
1554 } else {
1555 opcode << "mls";
1556 args << Rd << ", " << Rn << ", " << Rm << ", " << Ra;
1557 }
1558 break;
1559 case 1:
1560 case 2:
1561 case 3:
1562 case 4:
1563 case 5:
1564 case 6:
1565 break; // do these sometime
1566 }
1567 } else if ((op2 >> 3) == 7) { // 0111xxx
1568 // Long multiply, long multiply accumulate, and divide
1569 op1 = (instr >> 20) & 0x7;
1570 op2 = (instr >> 4) & 0xf;
1571 ArmRegister Rn(instr, 16);
1572 ArmRegister Rm(instr, 0);
1573 ArmRegister Rd(instr, 8);
1574 ArmRegister RdHi(instr, 8);
1575 ArmRegister RdLo(instr, 12);
1576 switch (op1) {
1577 case 0:
1578 opcode << "smull";
1579 args << RdLo << ", " << RdHi << ", " << Rn << ", " << Rm;
1580 break;
1581 case 1:
1582 opcode << "sdiv";
1583 args << Rd << ", " << Rn << ", " << Rm;
1584 break;
1585 case 2:
1586 opcode << "umull";
1587 args << RdLo << ", " << RdHi << ", " << Rn << ", " << Rm;
1588 break;
1589 case 3:
1590 opcode << "udiv";
1591 args << Rd << ", " << Rn << ", " << Rm;
1592 break;
1593 case 4:
1594 case 5:
1595 case 6:
1596 break; // TODO: when we generate these...
1597 }
1598 }
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001599 }
1600 default:
1601 break;
1602 }
Ian Rogers9af89402012-09-07 11:29:35 -07001603
1604 // Apply any IT-block conditions to the opcode if necessary.
1605 if (!it_conditions_.empty()) {
1606 opcode << it_conditions_.back();
1607 it_conditions_.pop_back();
1608 }
Nicolas Geoffray3c7bb982014-07-23 16:04:16 +01001609 if (opcode.str().size() == 0) {
1610 opcode << "UNKNOWN " << op2;
1611 }
Ian Rogers9af89402012-09-07 11:29:35 -07001612
Brian Carlstrom2cbaccb2014-09-14 20:34:17 -07001613 os << FormatInstructionPointer(instr_ptr)
1614 << StringPrintf(": %08x\t%-7s ", instr, opcode.str().c_str())
1615 << args.str() << '\n';
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001616 return 4;
Brian Carlstrom1895ea32013-07-18 13:28:37 -07001617} // NOLINT(readability/fn_size)
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001618
1619size_t DisassemblerArm::DumpThumb16(std::ostream& os, const uint8_t* instr_ptr) {
1620 uint16_t instr = ReadU16(instr_ptr);
1621 bool is_32bit = ((instr & 0xF000) == 0xF000) || ((instr & 0xF800) == 0xE800);
1622 if (is_32bit) {
1623 return DumpThumb32(os, instr_ptr);
1624 } else {
Elliott Hughescbf0b612012-03-15 16:23:47 -07001625 std::ostringstream opcode;
1626 std::ostringstream args;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001627 uint16_t opcode1 = instr >> 10;
1628 if (opcode1 < 0x10) {
1629 // shift (immediate), add, subtract, move, and compare
1630 uint16_t opcode2 = instr >> 9;
1631 switch (opcode2) {
1632 case 0x0: case 0x1: case 0x2: case 0x3: case 0x4: case 0x5: case 0x6: case 0x7:
1633 case 0x8: case 0x9: case 0xA: case 0xB: {
Sebastien Hertze78500c2013-02-19 14:29:52 +01001634 // Logical shift left - 00 000xx iii mmm ddd
1635 // Logical shift right - 00 001xx iii mmm ddd
1636 // Arithmetic shift right - 00 010xx iii mmm ddd
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001637 uint16_t imm5 = (instr >> 6) & 0x1F;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001638 ThumbRegister rm(instr, 3);
Sebastien Hertze78500c2013-02-19 14:29:52 +01001639 ThumbRegister Rd(instr, 0);
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001640 if (opcode2 <= 3) {
Elliott Hughescbf0b612012-03-15 16:23:47 -07001641 opcode << "lsls";
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001642 } else if (opcode2 <= 7) {
Elliott Hughescbf0b612012-03-15 16:23:47 -07001643 opcode << "lsrs";
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001644 } else {
Elliott Hughescbf0b612012-03-15 16:23:47 -07001645 opcode << "asrs";
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001646 }
Elliott Hughes630e77d2012-03-22 19:20:56 -07001647 args << Rd << ", " << rm << ", #" << imm5;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001648 break;
1649 }
1650 case 0xC: case 0xD: case 0xE: case 0xF: {
1651 // Add register - 00 01100 mmm nnn ddd
1652 // Sub register - 00 01101 mmm nnn ddd
1653 // Add 3-bit immediate - 00 01110 iii nnn ddd
1654 // Sub 3-bit immediate - 00 01111 iii nnn ddd
1655 uint16_t imm3_or_Rm = (instr >> 6) & 7;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001656 ThumbRegister Rn(instr, 3);
1657 ThumbRegister Rd(instr, 0);
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001658 if ((opcode2 & 2) != 0 && imm3_or_Rm == 0) {
Elliott Hughescbf0b612012-03-15 16:23:47 -07001659 opcode << "mov";
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001660 } else {
1661 if ((opcode2 & 1) == 0) {
Elliott Hughescbf0b612012-03-15 16:23:47 -07001662 opcode << "adds";
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001663 } else {
Elliott Hughescbf0b612012-03-15 16:23:47 -07001664 opcode << "subs";
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001665 }
1666 }
Elliott Hughes630e77d2012-03-22 19:20:56 -07001667 args << Rd << ", " << Rn;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001668 if ((opcode2 & 2) == 0) {
Elliott Hughes630e77d2012-03-22 19:20:56 -07001669 ArmRegister Rm(imm3_or_Rm);
1670 args << ", " << Rm;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001671 } else if (imm3_or_Rm != 0) {
Elliott Hughescbf0b612012-03-15 16:23:47 -07001672 args << ", #" << imm3_or_Rm;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001673 }
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001674 break;
1675 }
1676 case 0x10: case 0x11: case 0x12: case 0x13:
1677 case 0x14: case 0x15: case 0x16: case 0x17:
1678 case 0x18: case 0x19: case 0x1A: case 0x1B:
1679 case 0x1C: case 0x1D: case 0x1E: case 0x1F: {
1680 // MOVS Rd, #imm8 - 00100 ddd iiiiiiii
1681 // CMP Rn, #imm8 - 00101 nnn iiiiiiii
1682 // ADDS Rn, #imm8 - 00110 nnn iiiiiiii
1683 // SUBS Rn, #imm8 - 00111 nnn iiiiiiii
Elliott Hughes630e77d2012-03-22 19:20:56 -07001684 ThumbRegister Rn(instr, 8);
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001685 uint16_t imm8 = instr & 0xFF;
1686 switch (opcode2 >> 2) {
Elliott Hughescbf0b612012-03-15 16:23:47 -07001687 case 4: opcode << "movs"; break;
1688 case 5: opcode << "cmp"; break;
1689 case 6: opcode << "adds"; break;
1690 case 7: opcode << "subs"; break;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001691 }
Elliott Hughes630e77d2012-03-22 19:20:56 -07001692 args << Rn << ", #" << imm8;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001693 break;
1694 }
1695 default:
1696 break;
1697 }
Ian Rogersad03ef52012-03-18 19:34:47 -07001698 } else if (opcode1 == 0x10) {
1699 // Data-processing
1700 uint16_t opcode2 = (instr >> 6) & 0xF;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001701 ThumbRegister rm(instr, 3);
1702 ThumbRegister rdn(instr, 0);
Ian Rogersad03ef52012-03-18 19:34:47 -07001703 opcode << kThumbDataProcessingOperations[opcode2];
Elliott Hughes630e77d2012-03-22 19:20:56 -07001704 args << rdn << ", " << rm;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001705 } else if (opcode1 == 0x11) {
1706 // Special data instructions and branch and exchange
1707 uint16_t opcode2 = (instr >> 6) & 0x0F;
1708 switch (opcode2) {
1709 case 0x0: case 0x1: case 0x2: case 0x3: {
1710 // Add low registers - 010001 0000 xxxxxx
1711 // Add high registers - 010001 0001/001x xxxxxx
1712 uint16_t DN = (instr >> 7) & 1;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001713 ArmRegister rm(instr, 3);
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001714 uint16_t Rdn = instr & 7;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001715 ArmRegister DN_Rdn((DN << 3) | Rdn);
Elliott Hughescbf0b612012-03-15 16:23:47 -07001716 opcode << "add";
Elliott Hughes630e77d2012-03-22 19:20:56 -07001717 args << DN_Rdn << ", " << rm;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001718 break;
1719 }
1720 case 0x8: case 0x9: case 0xA: case 0xB: {
1721 // Move low registers - 010001 1000 xxxxxx
1722 // Move high registers - 010001 1001/101x xxxxxx
1723 uint16_t DN = (instr >> 7) & 1;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001724 ArmRegister rm(instr, 3);
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001725 uint16_t Rdn = instr & 7;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001726 ArmRegister DN_Rdn((DN << 3) | Rdn);
Elliott Hughescbf0b612012-03-15 16:23:47 -07001727 opcode << "mov";
Elliott Hughes630e77d2012-03-22 19:20:56 -07001728 args << DN_Rdn << ", " << rm;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001729 break;
1730 }
1731 case 0x5: case 0x6: case 0x7: {
1732 // Compare high registers - 010001 0101/011x xxxxxx
1733 uint16_t N = (instr >> 7) & 1;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001734 ArmRegister rm(instr, 3);
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001735 uint16_t Rn = instr & 7;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001736 ArmRegister N_Rn((N << 3) | Rn);
Elliott Hughescbf0b612012-03-15 16:23:47 -07001737 opcode << "cmp";
Elliott Hughes630e77d2012-03-22 19:20:56 -07001738 args << N_Rn << ", " << rm;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001739 break;
1740 }
1741 case 0xC: case 0xD: case 0xE: case 0xF: {
1742 // Branch and exchange - 010001 110x xxxxxx
1743 // Branch with link and exchange - 010001 111x xxxxxx
Elliott Hughes630e77d2012-03-22 19:20:56 -07001744 ArmRegister rm(instr, 3);
1745 opcode << ((opcode2 & 0x2) == 0 ? "bx" : "blx");
1746 args << rm;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001747 break;
1748 }
1749 default:
1750 break;
1751 }
jeffhaoeae26912013-01-28 16:29:54 -08001752 } else if (opcode1 == 0x12 || opcode1 == 0x13) { // 01001x
1753 ThumbRegister Rt(instr, 8);
1754 uint16_t imm8 = instr & 0xFF;
1755 opcode << "ldr";
1756 args << Rt << ", [pc, #" << (imm8 << 2) << "]";
Ian Rogersd83bc362012-09-07 17:43:13 -07001757 } else if ((opcode1 >= 0x14 && opcode1 <= 0x17) || // 0101xx
1758 (opcode1 >= 0x18 && opcode1 <= 0x1f) || // 011xxx
1759 (opcode1 >= 0x20 && opcode1 <= 0x27)) { // 100xxx
1760 // Load/store single data item
1761 uint16_t opA = (instr >> 12) & 0xF;
1762 if (opA == 0x5) {
1763 uint16_t opB = (instr >> 9) & 0x7;
1764 ThumbRegister Rm(instr, 6);
1765 ThumbRegister Rn(instr, 3);
1766 ThumbRegister Rt(instr, 0);
Brian Carlstromdf629502013-07-17 22:39:56 -07001767 switch (opB) {
Ian Rogersd83bc362012-09-07 17:43:13 -07001768 case 0: opcode << "str"; break;
1769 case 1: opcode << "strh"; break;
1770 case 2: opcode << "strb"; break;
1771 case 3: opcode << "ldrsb"; break;
1772 case 4: opcode << "ldr"; break;
1773 case 5: opcode << "ldrh"; break;
1774 case 6: opcode << "ldrb"; break;
1775 case 7: opcode << "ldrsh"; break;
1776 }
1777 args << Rt << ", [" << Rn << ", " << Rm << "]";
1778 } else if (opA == 9) {
1779 uint16_t opB = (instr >> 11) & 1;
1780 ThumbRegister Rt(instr, 8);
1781 uint16_t imm8 = instr & 0xFF;
1782 opcode << (opB == 0 ? "str" : "ldr");
Ian Rogers137e88f2012-10-08 17:46:47 -07001783 args << Rt << ", [sp, #" << (imm8 << 2) << "]";
Ian Rogersd83bc362012-09-07 17:43:13 -07001784 } else {
1785 uint16_t imm5 = (instr >> 6) & 0x1F;
1786 uint16_t opB = (instr >> 11) & 1;
1787 ThumbRegister Rn(instr, 3);
1788 ThumbRegister Rt(instr, 0);
Brian Carlstromdf629502013-07-17 22:39:56 -07001789 switch (opA) {
Ian Rogersd83bc362012-09-07 17:43:13 -07001790 case 6:
1791 imm5 <<= 2;
1792 opcode << (opB == 0 ? "str" : "ldr");
1793 break;
1794 case 7:
1795 imm5 <<= 0;
1796 opcode << (opB == 0 ? "strb" : "ldrb");
1797 break;
1798 case 8:
1799 imm5 <<= 1;
1800 opcode << (opB == 0 ? "strh" : "ldrh");
1801 break;
1802 }
1803 args << Rt << ", [" << Rn << ", #" << imm5 << "]";
1804 }
jeffhaoeae26912013-01-28 16:29:54 -08001805 } else if (opcode1 >= 0x34 && opcode1 <= 0x37) { // 1101xx
Ian Rogers7761cb62013-06-17 14:10:46 -07001806 int8_t imm8 = instr & 0xFF;
jeffhaoeae26912013-01-28 16:29:54 -08001807 uint32_t cond = (instr >> 8) & 0xF;
1808 opcode << "b";
1809 DumpCond(opcode, cond);
1810 DumpBranchTarget(args, instr_ptr + 4, (imm8 << 1));
Ian Rogers9af89402012-09-07 11:29:35 -07001811 } else if ((instr & 0xF800) == 0xA800) {
1812 // Generate SP-relative address
1813 ThumbRegister rd(instr, 8);
1814 int imm8 = instr & 0xFF;
1815 opcode << "add";
1816 args << rd << ", sp, #" << (imm8 << 2);
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001817 } else if ((instr & 0xF000) == 0xB000) {
1818 // Miscellaneous 16-bit instructions
1819 uint16_t opcode2 = (instr >> 5) & 0x7F;
1820 switch (opcode2) {
1821 case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: {
1822 // Add immediate to SP - 1011 00000 ii iiiii
1823 // Subtract immediate from SP - 1011 00001 ii iiiii
1824 int imm7 = instr & 0x7F;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001825 opcode << ((opcode2 & 4) == 0 ? "add" : "sub");
Elliott Hughescbf0b612012-03-15 16:23:47 -07001826 args << "sp, sp, #" << (imm7 << 2);
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001827 break;
1828 }
Ian Rogers087b2412012-03-21 01:30:32 -07001829 case 0x08: case 0x09: case 0x0A: case 0x0B: // 0001xxx
Ian Rogersebbc5772012-04-11 17:00:08 -07001830 case 0x0C: case 0x0D: case 0x0E: case 0x0F:
Ian Rogers55019132013-02-08 01:05:23 -08001831 case 0x18: case 0x19: case 0x1A: case 0x1B: // 0011xxx
1832 case 0x1C: case 0x1D: case 0x1E: case 0x1F:
Ian Rogersebbc5772012-04-11 17:00:08 -07001833 case 0x48: case 0x49: case 0x4A: case 0x4B: // 1001xxx
Ian Rogers55019132013-02-08 01:05:23 -08001834 case 0x4C: case 0x4D: case 0x4E: case 0x4F:
1835 case 0x58: case 0x59: case 0x5A: case 0x5B: // 1011xxx
1836 case 0x5C: case 0x5D: case 0x5E: case 0x5F: {
Ian Rogers087b2412012-03-21 01:30:32 -07001837 // CBNZ, CBZ
1838 uint16_t op = (instr >> 11) & 1;
1839 uint16_t i = (instr >> 9) & 1;
1840 uint16_t imm5 = (instr >> 3) & 0x1F;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001841 ThumbRegister Rn(instr, 0);
Ian Rogers087b2412012-03-21 01:30:32 -07001842 opcode << (op != 0 ? "cbnz" : "cbz");
Ian Rogers828a07f2013-06-18 22:27:34 -07001843 uint32_t imm32 = (i << 6) | (imm5 << 1);
Elliott Hughes630e77d2012-03-22 19:20:56 -07001844 args << Rn << ", ";
Ian Rogers087b2412012-03-21 01:30:32 -07001845 DumpBranchTarget(args, instr_ptr + 4, imm32);
1846 break;
1847 }
Vladimir Markoa8b4caf2013-10-24 15:08:57 +01001848 case 0x50: case 0x51: // 101000x
1849 case 0x52: case 0x53: // 101001x
1850 case 0x56: case 0x57: { // 101011x
1851 uint16_t op = (instr >> 6) & 3;
1852 opcode << kThumbReverseOperations[op];
1853 ThumbRegister Rm(instr, 3);
1854 ThumbRegister Rd(instr, 0);
1855 args << Rd << ", " << Rm;
1856 break;
1857 }
Ian Rogers40627db2012-03-04 17:31:09 -08001858 case 0x78: case 0x79: case 0x7A: case 0x7B: // 1111xxx
1859 case 0x7C: case 0x7D: case 0x7E: case 0x7F: {
1860 // If-Then, and hints
1861 uint16_t opA = (instr >> 4) & 0xF;
1862 uint16_t opB = instr & 0xF;
1863 if (opB == 0) {
1864 switch (opA) {
Elliott Hughescbf0b612012-03-15 16:23:47 -07001865 case 0: opcode << "nop"; break;
1866 case 1: opcode << "yield"; break;
1867 case 2: opcode << "wfe"; break;
1868 case 3: opcode << "sev"; break;
Ian Rogers40627db2012-03-04 17:31:09 -08001869 default: break;
1870 }
1871 } else {
Elliott Hughes105afd22012-04-10 15:04:25 -07001872 uint32_t first_cond = opA;
1873 uint32_t mask = opB;
Elliott Hughescbf0b612012-03-15 16:23:47 -07001874 opcode << "it";
Elliott Hughes105afd22012-04-10 15:04:25 -07001875
1876 // Flesh out the base "it" opcode with the specific collection of 't's and 'e's,
1877 // and store up the actual condition codes we'll want to add to the next few opcodes.
1878 size_t count = 3 - CTZ(mask);
Brian Carlstrom7934ac22013-07-26 10:54:15 -07001879 it_conditions_.resize(count + 2); // Plus the implicit 't', plus the "" for the IT itself.
Elliott Hughes105afd22012-04-10 15:04:25 -07001880 for (size_t i = 0; i < count; ++i) {
1881 bool positive_cond = ((first_cond & 1) != 0);
1882 bool positive_mask = ((mask & (1 << (3 - i))) != 0);
1883 if (positive_mask == positive_cond) {
1884 opcode << 't';
1885 it_conditions_[i] = kConditionCodeNames[first_cond];
1886 } else {
1887 opcode << 'e';
1888 it_conditions_[i] = kConditionCodeNames[first_cond ^ 1];
1889 }
1890 }
Brian Carlstrom7934ac22013-07-26 10:54:15 -07001891 it_conditions_[count] = kConditionCodeNames[first_cond]; // The implicit 't'.
Elliott Hughes105afd22012-04-10 15:04:25 -07001892
Brian Carlstrom7934ac22013-07-26 10:54:15 -07001893 it_conditions_[count + 1] = ""; // No condition code for the IT itself...
1894 DumpCond(args, first_cond); // ...because it's considered an argument.
Ian Rogers40627db2012-03-04 17:31:09 -08001895 }
1896 break;
1897 }
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001898 default:
1899 break;
1900 }
1901 } else if (((instr & 0xF000) == 0x5000) || ((instr & 0xE000) == 0x6000) ||
1902 ((instr & 0xE000) == 0x8000)) {
1903 // Load/store single data item
1904 uint16_t opA = instr >> 12;
Brian Carlstrom7934ac22013-07-26 10:54:15 -07001905 // uint16_t opB = (instr >> 9) & 7;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001906 switch (opA) {
1907 case 0x6: {
Elliott Hughes28fa76d2012-04-09 17:31:46 -07001908 // STR Rt, [Rn, #imm] - 01100 iiiii nnn ttt
1909 // LDR Rt, [Rn, #imm] - 01101 iiiii nnn ttt
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001910 uint16_t imm5 = (instr >> 6) & 0x1F;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001911 ThumbRegister Rn(instr, 3);
Elliott Hughes28fa76d2012-04-09 17:31:46 -07001912 ThumbRegister Rt(instr, 0);
Elliott Hughes630e77d2012-03-22 19:20:56 -07001913 opcode << ((instr & 0x800) == 0 ? "str" : "ldr");
1914 args << Rt << ", [" << Rn << ", #" << (imm5 << 2) << "]";
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001915 break;
1916 }
1917 case 0x9: {
1918 // STR Rt, [SP, #imm] - 01100 ttt iiiiiiii
1919 // LDR Rt, [SP, #imm] - 01101 ttt iiiiiiii
1920 uint16_t imm8 = instr & 0xFF;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001921 ThumbRegister Rt(instr, 8);
1922 opcode << ((instr & 0x800) == 0 ? "str" : "ldr");
1923 args << Rt << ", [sp, #" << (imm8 << 2) << "]";
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001924 break;
1925 }
1926 default:
1927 break;
1928 }
Ian Rogers40627db2012-03-04 17:31:09 -08001929 } else if (opcode1 == 0x38 || opcode1 == 0x39) {
1930 uint16_t imm11 = instr & 0x7FFF;
1931 int32_t imm32 = imm11 << 1;
1932 imm32 = (imm32 << 20) >> 20; // sign extend 12 bit immediate
Elliott Hughescbf0b612012-03-15 16:23:47 -07001933 opcode << "b";
1934 DumpBranchTarget(args, instr_ptr + 4, imm32);
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001935 }
Elliott Hughes105afd22012-04-10 15:04:25 -07001936
1937 // Apply any IT-block conditions to the opcode if necessary.
1938 if (!it_conditions_.empty()) {
1939 opcode << it_conditions_.back();
1940 it_conditions_.pop_back();
1941 }
1942
Brian Carlstrom2cbaccb2014-09-14 20:34:17 -07001943 os << FormatInstructionPointer(instr_ptr)
1944 << StringPrintf(": %04x \t%-7s ", instr, opcode.str().c_str())
1945 << args.str() << '\n';
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001946 }
1947 return 2;
1948}
1949
1950} // namespace arm
1951} // namespace art