blob: 4fb061f351978feb4365698ab4a431d107eff98c [file] [log] [blame]
Carl Shapiroa5d5cfd2011-06-21 12:46:59 -07001// Copyright 2011 Google Inc. All Rights Reserved.
2
3#ifndef ART_SRC_ASSEMBLER_ARM_H_
4#define ART_SRC_ASSEMBLER_ARM_H_
5
Ian Rogersb033c752011-07-20 12:22:35 -07006#include "src/constants.h"
7#include "src/managed_register.h"
Carl Shapiroa2e18e12011-06-21 18:57:55 -07008#include "src/logging.h"
9#include "src/utils.h"
10
Carl Shapiro6b6b5f02011-06-21 15:05:09 -070011namespace art {
Carl Shapiroa5d5cfd2011-06-21 12:46:59 -070012
Carl Shapiroa2e18e12011-06-21 18:57:55 -070013// Encodes Addressing Mode 1 - Data-processing operands defined in Section 5.1.
14class ShifterOperand {
15 public:
16 // Data-processing operands - Uninitialized
17 ShifterOperand() {
18 type_ = -1;
19 }
20
21 // Data-processing operands - Immediate
22 explicit ShifterOperand(uint32_t immediate) {
23 CHECK(immediate < (1 << kImmed8Bits));
24 type_ = 1;
25 encoding_ = immediate;
26 }
27
28 // Data-processing operands - Rotated immediate
29 ShifterOperand(uint32_t rotate, uint32_t immed8) {
30 CHECK((rotate < (1 << kRotateBits)) && (immed8 < (1 << kImmed8Bits)));
31 type_ = 1;
32 encoding_ = (rotate << kRotateShift) | (immed8 << kImmed8Shift);
33 }
34
35 // Data-processing operands - Register
36 explicit ShifterOperand(Register rm) {
37 type_ = 0;
38 encoding_ = static_cast<uint32_t>(rm);
39 }
40
41 // Data-processing operands - Logical shift/rotate by immediate
42 ShifterOperand(Register rm, Shift shift, uint32_t shift_imm) {
43 CHECK(shift_imm < (1 << kShiftImmBits));
44 type_ = 0;
45 encoding_ = shift_imm << kShiftImmShift |
46 static_cast<uint32_t>(shift) << kShiftShift |
47 static_cast<uint32_t>(rm);
48 }
49
50 // Data-processing operands - Logical shift/rotate by register
51 ShifterOperand(Register rm, Shift shift, Register rs) {
52 type_ = 0;
53 encoding_ = static_cast<uint32_t>(rs) << kShiftRegisterShift |
54 static_cast<uint32_t>(shift) << kShiftShift | (1 << 4) |
55 static_cast<uint32_t>(rm);
56 }
57
58 static bool CanHold(uint32_t immediate, ShifterOperand* shifter_op) {
59 // Avoid the more expensive test for frequent small immediate values.
60 if (immediate < (1 << kImmed8Bits)) {
61 shifter_op->type_ = 1;
62 shifter_op->encoding_ = (0 << kRotateShift) | (immediate << kImmed8Shift);
63 return true;
64 }
65 // Note that immediate must be unsigned for the test to work correctly.
66 for (int rot = 0; rot < 16; rot++) {
67 uint32_t imm8 = (immediate << 2*rot) | (immediate >> (32 - 2*rot));
68 if (imm8 < (1 << kImmed8Bits)) {
69 shifter_op->type_ = 1;
70 shifter_op->encoding_ = (rot << kRotateShift) | (imm8 << kImmed8Shift);
71 return true;
72 }
73 }
74 return false;
75 }
76
77 private:
78 bool is_valid() const { return (type_ == 0) || (type_ == 1); }
79
80 uint32_t type() const {
81 CHECK(is_valid());
82 return type_;
83 }
84
85 uint32_t encoding() const {
86 CHECK(is_valid());
87 return encoding_;
88 }
89
90 uint32_t type_; // Encodes the type field (bits 27-25) in the instruction.
91 uint32_t encoding_;
92
93 friend class Assembler;
94#ifdef SOURCE_ASSEMBLER_SUPPORT
95 friend class BinaryAssembler;
96#endif
97};
98
99
100enum LoadOperandType {
101 kLoadSignedByte,
102 kLoadUnsignedByte,
103 kLoadSignedHalfword,
104 kLoadUnsignedHalfword,
105 kLoadWord,
106 kLoadWordPair,
107 kLoadSWord,
108 kLoadDWord
109};
110
111
112enum StoreOperandType {
113 kStoreByte,
114 kStoreHalfword,
115 kStoreWord,
116 kStoreWordPair,
117 kStoreSWord,
118 kStoreDWord
119};
120
121
122// Load/store multiple addressing mode.
123enum BlockAddressMode {
124 // bit encoding P U W
125 DA = (0|0|0) << 21, // decrement after
126 IA = (0|4|0) << 21, // increment after
127 DB = (8|0|0) << 21, // decrement before
128 IB = (8|4|0) << 21, // increment before
129 DA_W = (0|0|1) << 21, // decrement after with writeback to base
130 IA_W = (0|4|1) << 21, // increment after with writeback to base
131 DB_W = (8|0|1) << 21, // decrement before with writeback to base
132 IB_W = (8|4|1) << 21 // increment before with writeback to base
133};
134
135
136class Address {
137 public:
138 // Memory operand addressing mode
139 enum Mode {
140 // bit encoding P U W
141 Offset = (8|4|0) << 21, // offset (w/o writeback to base)
142 PreIndex = (8|4|1) << 21, // pre-indexed addressing with writeback
143 PostIndex = (0|4|0) << 21, // post-indexed addressing with writeback
144 NegOffset = (8|0|0) << 21, // negative offset (w/o writeback to base)
145 NegPreIndex = (8|0|1) << 21, // negative pre-indexed with writeback
146 NegPostIndex = (0|0|0) << 21 // negative post-indexed with writeback
147 };
148
149 explicit Address(Register rn, int32_t offset = 0, Mode am = Offset) {
150 CHECK(IsAbsoluteUint(12, offset));
151 if (offset < 0) {
152 encoding_ = (am ^ (1 << kUShift)) | -offset; // Flip U to adjust sign.
153 } else {
154 encoding_ = am | offset;
155 }
156 encoding_ |= static_cast<uint32_t>(rn) << kRnShift;
157 }
158
159 static bool CanHoldLoadOffset(LoadOperandType type, int offset);
160 static bool CanHoldStoreOffset(StoreOperandType type, int offset);
161
162 private:
163 uint32_t encoding() const { return encoding_; }
164
165 // Encoding for addressing mode 3.
166 uint32_t encoding3() const {
167 const uint32_t offset_mask = (1 << 12) - 1;
168 uint32_t offset = encoding_ & offset_mask;
Ian Rogersb033c752011-07-20 12:22:35 -0700169 CHECK_LT(offset, 256u);
Carl Shapiroa2e18e12011-06-21 18:57:55 -0700170 return (encoding_ & ~offset_mask) | ((offset & 0xf0) << 4) | (offset & 0xf);
171 }
172
173 // Encoding for vfp load/store addressing.
174 uint32_t vencoding() const {
175 const uint32_t offset_mask = (1 << 12) - 1;
176 uint32_t offset = encoding_ & offset_mask;
177 CHECK(IsAbsoluteUint(10, offset)); // In the range -1020 to +1020.
178 CHECK(IsAligned(offset, 2)); // Multiple of 4.
179 int mode = encoding_ & ((8|4|1) << 21);
180 CHECK((mode == Offset) || (mode == NegOffset));
181 uint32_t vencoding = (encoding_ & (0xf << kRnShift)) | (offset >> 2);
182 if (mode == Offset) {
183 vencoding |= 1 << 23;
184 }
185 return vencoding;
186 }
187
188 uint32_t encoding_;
189
190 friend class Assembler;
191};
192
193
Carl Shapiroa5d5cfd2011-06-21 12:46:59 -0700194class Assembler {
Carl Shapiroa2e18e12011-06-21 18:57:55 -0700195 public:
196 Assembler() : buffer_() {}
197
198 // Data-processing instructions.
199 void and_(Register rd, Register rn, ShifterOperand so, Condition cond = AL);
200
201 void eor(Register rd, Register rn, ShifterOperand so, Condition cond = AL);
202
203 void sub(Register rd, Register rn, ShifterOperand so, Condition cond = AL);
204 void subs(Register rd, Register rn, ShifterOperand so, Condition cond = AL);
205
206 void rsb(Register rd, Register rn, ShifterOperand so, Condition cond = AL);
207 void rsbs(Register rd, Register rn, ShifterOperand so, Condition cond = AL);
208
209 void add(Register rd, Register rn, ShifterOperand so, Condition cond = AL);
210
211 void adds(Register rd, Register rn, ShifterOperand so, Condition cond = AL);
212
213 void adc(Register rd, Register rn, ShifterOperand so, Condition cond = AL);
214
215 void sbc(Register rd, Register rn, ShifterOperand so, Condition cond = AL);
216
217 void rsc(Register rd, Register rn, ShifterOperand so, Condition cond = AL);
218
219 void tst(Register rn, ShifterOperand so, Condition cond = AL);
220
221 void teq(Register rn, ShifterOperand so, Condition cond = AL);
222
223 void cmp(Register rn, ShifterOperand so, Condition cond = AL);
224
225 void cmn(Register rn, ShifterOperand so, Condition cond = AL);
226
227 void orr(Register rd, Register rn, ShifterOperand so, Condition cond = AL);
228 void orrs(Register rd, Register rn, ShifterOperand so, Condition cond = AL);
229
230 void mov(Register rd, ShifterOperand so, Condition cond = AL);
231 void movs(Register rd, ShifterOperand so, Condition cond = AL);
232
233 void bic(Register rd, Register rn, ShifterOperand so, Condition cond = AL);
234
235 void mvn(Register rd, ShifterOperand so, Condition cond = AL);
236 void mvns(Register rd, ShifterOperand so, Condition cond = AL);
237
238 // Miscellaneous data-processing instructions.
239 void clz(Register rd, Register rm, Condition cond = AL);
240 void movw(Register rd, uint16_t imm16, Condition cond = AL);
241 void movt(Register rd, uint16_t imm16, Condition cond = AL);
242
243 // Multiply instructions.
244 void mul(Register rd, Register rn, Register rm, Condition cond = AL);
245 void mla(Register rd, Register rn, Register rm, Register ra,
246 Condition cond = AL);
247 void mls(Register rd, Register rn, Register rm, Register ra,
248 Condition cond = AL);
249 void umull(Register rd_lo, Register rd_hi, Register rn, Register rm,
250 Condition cond = AL);
251
252 // Load/store instructions.
253 void ldr(Register rd, Address ad, Condition cond = AL);
254 void str(Register rd, Address ad, Condition cond = AL);
255
256 void ldrb(Register rd, Address ad, Condition cond = AL);
257 void strb(Register rd, Address ad, Condition cond = AL);
258
259 void ldrh(Register rd, Address ad, Condition cond = AL);
260 void strh(Register rd, Address ad, Condition cond = AL);
261
262 void ldrsb(Register rd, Address ad, Condition cond = AL);
263 void ldrsh(Register rd, Address ad, Condition cond = AL);
264
265 void ldrd(Register rd, Address ad, Condition cond = AL);
266 void strd(Register rd, Address ad, Condition cond = AL);
267
268 void ldm(BlockAddressMode am, Register base,
269 RegList regs, Condition cond = AL);
270 void stm(BlockAddressMode am, Register base,
271 RegList regs, Condition cond = AL);
272
273 void ldrex(Register rd, Register rn, Condition cond = AL);
274 void strex(Register rd, Register rt, Register rn, Condition cond = AL);
275
276 // Miscellaneous instructions.
277 void clrex();
278 void nop(Condition cond = AL);
279
280 // Note that gdb sets breakpoints using the undefined instruction 0xe7f001f0.
281 void bkpt(uint16_t imm16);
282 void svc(uint32_t imm24);
283
284 // Floating point instructions (VFPv3-D16 and VFPv3-D32 profiles).
285 void vmovsr(SRegister sn, Register rt, Condition cond = AL);
286 void vmovrs(Register rt, SRegister sn, Condition cond = AL);
287 void vmovsrr(SRegister sm, Register rt, Register rt2, Condition cond = AL);
288 void vmovrrs(Register rt, Register rt2, SRegister sm, Condition cond = AL);
289 void vmovdrr(DRegister dm, Register rt, Register rt2, Condition cond = AL);
290 void vmovrrd(Register rt, Register rt2, DRegister dm, Condition cond = AL);
291 void vmovs(SRegister sd, SRegister sm, Condition cond = AL);
292 void vmovd(DRegister dd, DRegister dm, Condition cond = AL);
293
294 // Returns false if the immediate cannot be encoded.
295 bool vmovs(SRegister sd, float s_imm, Condition cond = AL);
296 bool vmovd(DRegister dd, double d_imm, Condition cond = AL);
297
298 void vldrs(SRegister sd, Address ad, Condition cond = AL);
299 void vstrs(SRegister sd, Address ad, Condition cond = AL);
300 void vldrd(DRegister dd, Address ad, Condition cond = AL);
301 void vstrd(DRegister dd, Address ad, Condition cond = AL);
302
303 void vadds(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL);
304 void vaddd(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL);
305 void vsubs(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL);
306 void vsubd(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL);
307 void vmuls(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL);
308 void vmuld(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL);
309 void vmlas(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL);
310 void vmlad(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL);
311 void vmlss(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL);
312 void vmlsd(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL);
313 void vdivs(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL);
314 void vdivd(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL);
315
316 void vabss(SRegister sd, SRegister sm, Condition cond = AL);
317 void vabsd(DRegister dd, DRegister dm, Condition cond = AL);
318 void vnegs(SRegister sd, SRegister sm, Condition cond = AL);
319 void vnegd(DRegister dd, DRegister dm, Condition cond = AL);
320 void vsqrts(SRegister sd, SRegister sm, Condition cond = AL);
321 void vsqrtd(DRegister dd, DRegister dm, Condition cond = AL);
322
323 void vcvtsd(SRegister sd, DRegister dm, Condition cond = AL);
324 void vcvtds(DRegister dd, SRegister sm, Condition cond = AL);
325 void vcvtis(SRegister sd, SRegister sm, Condition cond = AL);
326 void vcvtid(SRegister sd, DRegister dm, Condition cond = AL);
327 void vcvtsi(SRegister sd, SRegister sm, Condition cond = AL);
328 void vcvtdi(DRegister dd, SRegister sm, Condition cond = AL);
329 void vcvtus(SRegister sd, SRegister sm, Condition cond = AL);
330 void vcvtud(SRegister sd, DRegister dm, Condition cond = AL);
331 void vcvtsu(SRegister sd, SRegister sm, Condition cond = AL);
332 void vcvtdu(DRegister dd, SRegister sm, Condition cond = AL);
333
334 void vcmps(SRegister sd, SRegister sm, Condition cond = AL);
335 void vcmpd(DRegister dd, DRegister dm, Condition cond = AL);
336 void vcmpsz(SRegister sd, Condition cond = AL);
337 void vcmpdz(DRegister dd, Condition cond = AL);
338 void vmstat(Condition cond = AL); // VMRS APSR_nzcv, FPSCR
339
340 // Branch instructions.
341 void b(Label* label, Condition cond = AL);
342 void bl(Label* label, Condition cond = AL);
343 void blx(Register rm, Condition cond = AL);
344
345 // Macros.
346 // Add signed constant value to rd. May clobber IP.
347 void AddConstant(Register rd, int32_t value, Condition cond = AL);
348 void AddConstant(Register rd, Register rn, int32_t value,
349 Condition cond = AL);
350 void AddConstantSetFlags(Register rd, Register rn, int32_t value,
351 Condition cond = AL);
352 void AddConstantWithCarry(Register rd, Register rn, int32_t value,
353 Condition cond = AL);
354
355 // Load and Store. May clobber IP.
356 void LoadImmediate(Register rd, int32_t value, Condition cond = AL);
357 void LoadSImmediate(SRegister sd, float value, Condition cond = AL);
358 void LoadDImmediate(DRegister dd, double value,
359 Register scratch, Condition cond = AL);
360 void MarkExceptionHandler(Label* label);
361 void LoadFromOffset(LoadOperandType type,
362 Register reg,
363 Register base,
364 int32_t offset,
365 Condition cond = AL);
366 void StoreToOffset(StoreOperandType type,
367 Register reg,
368 Register base,
369 int32_t offset,
370 Condition cond = AL);
371 void LoadSFromOffset(SRegister reg,
372 Register base,
373 int32_t offset,
374 Condition cond = AL);
375 void StoreSToOffset(SRegister reg,
376 Register base,
377 int32_t offset,
378 Condition cond = AL);
379 void LoadDFromOffset(DRegister reg,
380 Register base,
381 int32_t offset,
382 Condition cond = AL);
383 void StoreDToOffset(DRegister reg,
384 Register base,
385 int32_t offset,
386 Condition cond = AL);
387
388 void Push(Register rd, Condition cond = AL);
389 void Pop(Register rd, Condition cond = AL);
390
391 void PushList(RegList regs, Condition cond = AL);
392 void PopList(RegList regs, Condition cond = AL);
393
394 void Mov(Register rd, Register rm, Condition cond = AL);
395
396 // Convenience shift instructions. Use mov instruction with shifter operand
397 // for variants setting the status flags or using a register shift count.
398 void Lsl(Register rd, Register rm, uint32_t shift_imm, Condition cond = AL);
399 void Lsr(Register rd, Register rm, uint32_t shift_imm, Condition cond = AL);
400 void Asr(Register rd, Register rm, uint32_t shift_imm, Condition cond = AL);
401 void Ror(Register rd, Register rm, uint32_t shift_imm, Condition cond = AL);
402 void Rrx(Register rd, Register rm, Condition cond = AL);
403
404 // Encode a signed constant in tst instructions, only affecting the flags.
405 void EncodeUint32InTstInstructions(uint32_t data);
406 // ... and decode from a pc pointing to the start of encoding instructions.
407 static uint32_t DecodeUint32FromTstInstructions(uword pc);
408 static bool IsInstructionForExceptionHandling(uword pc);
409
410 // Debugging and bringup support.
411 void Stop(const char* message);
412 void Unimplemented(const char* message);
413 void Untested(const char* message);
414 void Unreachable(const char* message);
415
Ian Rogersb033c752011-07-20 12:22:35 -0700416 // Emit code that will create an activation on the stack
417 void BuildFrame(size_t frame_size, ManagedRegister method_reg);
418
419 // Emit code that will remove an activation from the stack
420 void RemoveFrame(size_t frame_size);
421
422 void IncreaseFrameSize(size_t adjust);
423 void DecreaseFrameSize(size_t adjust);
424
425 // Store bytes from the given register onto the stack
426 void Store(FrameOffset dest, ManagedRegister src, size_t size);
Ian Rogersb033c752011-07-20 12:22:35 -0700427 void StoreRef(FrameOffset dest, ManagedRegister src);
Ian Rogersdf20fe02011-07-20 20:34:16 -0700428 void StoreRawPtr(FrameOffset dest, ManagedRegister src);
429
Ian Rogersb033c752011-07-20 12:22:35 -0700430 void CopyRef(FrameOffset dest, FrameOffset src, ManagedRegister scratch);
431 void LoadRef(ManagedRegister dest, ManagedRegister base, MemberOffset offs);
432
433 void StoreImmediateToFrame(FrameOffset dest, uint32_t imm,
434 ManagedRegister scratch);
435 void StoreImmediateToThread(ThreadOffset dest, uint32_t imm,
436 ManagedRegister scratch);
437
438 void Load(ManagedRegister dest, FrameOffset src, size_t size);
439
440 void LoadRawPtrFromThread(ManagedRegister dest, ThreadOffset offs);
441 void CopyRawPtrFromThread(FrameOffset fr_offs, ThreadOffset thr_offs,
442 ManagedRegister scratch);
443 void CopyRawPtrToThread(ThreadOffset thr_offs, FrameOffset fr_offs,
444 ManagedRegister scratch);
445
446 void StoreStackOffsetToThread(ThreadOffset thr_offs, FrameOffset fr_offs,
447 ManagedRegister scratch);
448
449 void Move(ManagedRegister dest, ManagedRegister src);
450 void Copy(FrameOffset dest, FrameOffset src, ManagedRegister scratch,
451 size_t size);
452 void CreateStackHandle(ManagedRegister out_reg, FrameOffset handle_offset,
453 ManagedRegister in_reg, bool null_allowed);
454
455 void CreateStackHandle(FrameOffset out_off, FrameOffset handle_offset,
456 ManagedRegister scratch, bool null_allowed);
457
Ian Rogersdf20fe02011-07-20 20:34:16 -0700458 void LoadReferenceFromStackHandle(ManagedRegister dst, ManagedRegister src);
Ian Rogersb033c752011-07-20 12:22:35 -0700459
460 void ValidateRef(ManagedRegister src, bool could_be_null);
461
462 void ValidateRef(FrameOffset src, bool could_be_null);
463
Ian Rogersdf20fe02011-07-20 20:34:16 -0700464 void Call(ManagedRegister base, Offset offset, ManagedRegister scratch);
Ian Rogersb033c752011-07-20 12:22:35 -0700465
Carl Shapiroa2e18e12011-06-21 18:57:55 -0700466 // Emit data (e.g. encoded instruction or immediate) to the
467 // instruction stream.
468 void Emit(int32_t value);
469
470 void Bind(Label* label);
471
Ian Rogersb033c752011-07-20 12:22:35 -0700472 size_t CodeSize() const { return buffer_.Size(); }
473
474 void FinalizeInstructions(const MemoryRegion& region) {
475 buffer_.FinalizeInstructions(region);
476 }
477
Carl Shapiroa2e18e12011-06-21 18:57:55 -0700478 private:
479 AssemblerBuffer buffer_;
480
481 void EmitType01(Condition cond,
482 int type,
483 Opcode opcode,
484 int set_cc,
485 Register rn,
486 Register rd,
487 ShifterOperand so);
488
489 void EmitType5(Condition cond, int offset, bool link);
490
491 void EmitMemOp(Condition cond,
492 bool load,
493 bool byte,
494 Register rd,
495 Address ad);
496
497 void EmitMemOpAddressMode3(Condition cond,
498 int32_t mode,
499 Register rd,
500 Address ad);
501
502 void EmitMultiMemOp(Condition cond,
503 BlockAddressMode am,
504 bool load,
505 Register base,
506 RegList regs);
507
508 void EmitShiftImmediate(Condition cond,
509 Shift opcode,
510 Register rd,
511 Register rm,
512 ShifterOperand so);
513
514 void EmitShiftRegister(Condition cond,
515 Shift opcode,
516 Register rd,
517 Register rm,
518 ShifterOperand so);
519
520 void EmitMulOp(Condition cond,
521 int32_t opcode,
522 Register rd,
523 Register rn,
524 Register rm,
525 Register rs);
526
527 void EmitVFPsss(Condition cond,
528 int32_t opcode,
529 SRegister sd,
530 SRegister sn,
531 SRegister sm);
532
533 void EmitVFPddd(Condition cond,
534 int32_t opcode,
535 DRegister dd,
536 DRegister dn,
537 DRegister dm);
538
539 void EmitVFPsd(Condition cond,
540 int32_t opcode,
541 SRegister sd,
542 DRegister dm);
543
544 void EmitVFPds(Condition cond,
545 int32_t opcode,
546 DRegister dd,
547 SRegister sm);
548
549 void EmitBranch(Condition cond, Label* label, bool link);
550 static int32_t EncodeBranchOffset(int offset, int32_t inst);
551 static int DecodeBranchOffset(int32_t inst);
552 int32_t EncodeTstOffset(int offset, int32_t inst);
553 int DecodeTstOffset(int32_t inst);
554
555 // Returns whether or not the given register is used for passing parameters.
556 static int RegisterCompare(const Register* reg1, const Register* reg2) {
557 return *reg1 - *reg2;
558 }
Carl Shapiroa5d5cfd2011-06-21 12:46:59 -0700559};
560
Ian Rogersb033c752011-07-20 12:22:35 -0700561} // namespace art
Carl Shapiroa5d5cfd2011-06-21 12:46:59 -0700562
563#endif // ART_SRC_ASSEMBLER_ARM_H_