CACAO
Aarch64Instructions.cpp
Go to the documentation of this file.
1 /* src/vm/jit/compiler2/aarch64/Aarch64Instructions.cpp
2 
3  Copyright (C) 2013
4  CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
5 
6  This file is part of CACAO.
7 
8  This program is free software; you can redistribute it and/or
9  modify it under the terms of the GNU General Public License as
10  published by the Free Software Foundation; either version 2, or (at
11  your option) any later version.
12 
13  This program is distributed in the hope that it will be useful, but
14  WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with this program; if not, write to the Free Software
20  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21  02110-1301, USA.
22 
23 */
24 
31 
32 #include "toolbox/logging.hpp"
33 
34 #define DEBUG_NAME "compiler2/aarch64"
35 
36 namespace cacao {
37 namespace jit {
38 namespace compiler2 {
39 namespace aarch64 {
40 
41 
42 // TODO: find out how I can write a u4 to cm using existing methods
43 static void emitRaw(CodeMemory* cm, u4 inst) {
44  CodeFragment cf = cm->get_CodeFragment(4);
45  cf[0] = inst & 0xff;
46  cf[1] = (inst >> 8) & 0xff;
47  cf[2] = (inst >> 16) & 0xff;
48  cf[3] = (inst >> 24) & 0xff;
49 }
50 
51 void LoadInst::emit(Emitter& em) const {
52  s4 off = this->offset();
53  Reg dst = this->reg_res();
54  Reg base = this->reg_base();
55 
56  u1 sz = 1 << dst.size(); /* 64bit: 8, 32bit: 4 */
57 
58  // Handle ambigous case first (see Armv8 reference manual)
59  if (off >= 0 && off <= 255 && (off % sz == 0)) {
60  em.ldr(dst, base, off);
61  } else if (off >= -256 && off <= 255) {
62  em.ldur(dst, base, off);
63  } else if (off < 0 && (-off) < 0xffff) { // this is for larger negative offsets
64  // TODO: R9 is just temporary fixed, as the offset should be known when
65  // this instruction is created, an additional vreg operator is needed here to
66  // temporarly hold this negative offset (maybe this can be the same es the dst reg)
67  em.movn(Reg::X(9), (-off) - 1);
68  em.ldr(dst, base, Reg::X(9));
69  } else {
70  em.ldr(dst, base, off);
71  }
72 }
73 
74 
75 void StoreInst::emit(Emitter& em) const {
76  s4 off = offset();
77  Reg src = reg_src();
78  Reg base = reg_base();
79  if (off < 0) {
80  em.stur(src, base, off);
81  } else {
82  em.str(src, base, off);
83  }
84 }
85 
86 
87 void MovInst::emit(Emitter& em) const {
88  // TODO: tell reg alloc that my MOVs cant handle stack slots ...
89  if (operands[0].op->is_Register() && result.op->is_Register()) {
90  if (reg_res().r() == reg_op(0).r()) return;
91 
92  em.mov(reg_res(), reg_op(0));
93  } else if (operands[0].op->is_Register() && result.op->is_stackslot()) {
95  str.emit(em);
96  } else {
97  LoadInst load(DstOp(result.op), SrcOp(operands[0].op), resultT());
98  load.emit(em);
99  }
100 }
101 
102 
103 void MovImmInst::emit(Emitter& em) const {
104  if (resultT() == Type::IntTypeID) emitIConst(em);
105  else emitLConst(em);
106 }
107 
109  Immediate* immediate = cast_to<Immediate>(this->get(0).op);
110  s4 value = immediate->get_value<s4>();
111  Reg reg = this->reg_res();
112 
113  // For small negative immediates, use MOVN
114  if (value < 0 && -value-1 < 0xffff) {
115  em.movn(reg, -value - 1);
116  return;
117  }
118 
119  em.movz(reg, value & 0xffff);
120 
121  u4 v = (u4) value;
122  if (v > 0xffff) {
123  u2 imm = (value >> 16) & 0xffff;
124  em.movk(reg, imm, 1);
125  }
126 }
127 
128 
130  Immediate* immediate = cast_to<Immediate>(this->get(0).op);
131  s8 value = immediate->get_value<s8>();
132  Reg reg = this->reg_res();
133 
134  // For small negative immediates, use MOVN
135  if (value < 0 && -value-1 < 0xffff) {
136  em.movn(reg, -value - 1);
137  return;
138  }
139 
140  em.movz(reg, value & 0xffff);
141 
142  u8 v = (u8) value;
143  if (v > 0xffff) {
144  u2 imm = (value >> 16) & 0xffff;
145  em.movk(reg, imm, 1);
146  }
147 
148  if (v > 0xffffffff) {
149  u2 imm = (value >> 32) & 0xffff;
150  em.movk(reg, imm, 2);
151  }
152 
153  if (v > 0xffffffffffff) {
154  u2 imm = (value >> 48) & 0xffff;
155  em.movk(reg, imm, 3);
156  }
157 }
158 
159 void CSelInst::emit(Emitter& em) const {
160  em.csel(reg_res(), reg_op(0), reg_op(1), cond);
161 }
162 
163 void AndInst::emit(Emitter& em) const {
164  em.andd(reg_res(), reg_op(0), reg_op(1));
165 }
166 
167 void CmpInst::emit(Emitter& em) const {
168  Reg reg0 = this->reg_op(0);
169 
170  if (this->get(1).op->is_Register()) {
171  em.cmp(reg0, this->reg_op(1));
172  } else if (this->get(1).op->is_Immediate()) {
173  Immediate *imm = cast_to<Immediate>(this->get(1).op);
174  em.cmp(reg0, imm->get_Int());
175  }
176 }
177 
178 void MulInst::emit(Emitter& em) const {
179  Reg res = reg_res();
180  em.mul(res, reg_op(0), reg_op(1));
181 
182  if (resultT() == Type::IntTypeID) {
183  em.ubfx(res, res);
184  }
185 }
186 
187 void AddInst::emit(Emitter& em) const {
188  if (get(0).op->is_Register() && get(1).op->is_Register()) {
189  em.add(reg_res(), reg_op(0), reg_op(1), shift, amount);
190  } else {
191  ABORT_MSG("AddInst<T>::emit", "Operand not in register.");
192  }
193 }
194 
195 void SubInst::emit(Emitter& em) const {
196  if (get(1).op->is_Register()) {
197  em.sub(reg_res(), reg_op(0), reg_op(1));
198  } else {
199  Immediate *imm = cast_to<Immediate>(get(1).op);
200  em.sub(reg_res(), reg_op(0), imm->get_Long());
201  }
202 }
203 
204 void NegInst::emit(Emitter& em) const { em.neg(reg_res(), reg_op(0)); }
205 
206 void DivInst::emit(Emitter& em) const {
207  em.sdiv(reg_res(), reg_op(0), reg_op(1));
208 }
209 
210 void MulSubInst::emit(Emitter& em) const {
211  em.msub(reg_res(), reg_op(0), reg_op(1), reg_op(2));
212 }
213 
214 void FAddInst::emit(Emitter& em) const {
215  em.fadd(reg_res(), reg_op(0), reg_op(1));
216 }
217 
218 void FSubInst::emit(Emitter& em) const {
219  em.fsub(reg_res(), reg_op(0), reg_op(1));
220 }
221 
222 void FNegInst::emit(Emitter& em) const { em.fneg(reg_res(), reg_op(0)); }
223 
224 void FMulInst::emit(Emitter& em) const {
225  em.fmul(reg_res(), reg_op(0), reg_op(1));
226 }
227 
228 void FDivInst::emit(Emitter& em) const {
229  em.fdiv(reg_res(), reg_op(0), reg_op(1));
230 }
231 
232 void FCmpInst::emit(Emitter& em) const {
233  em.fcmp(reg_op(0), reg_op(1));
234 }
235 
236 void FMovInst::emit(Emitter& em) const {
237  if (reg_res().r() == reg_op(0).r()) return;
238 
239  em.fmov(reg_res(), reg_op(0));
240 }
241 
242 void JumpInst::emit(CodeMemory* cm) const {
244  CodeSegment &CS = cm->get_CodeSegment();
245  CodeSegment::IdxTy idx = CS.get_index(CSLabel(MBB));
246  if (CodeSegment::is_invalid(idx)) {
247  LOG2("JumpInst::emit: target not yet known (" << this << " to "
248  << *MBB << ")" << nl);
249 
250  // reserve memory and add to resolve later
251  CodeFragment cf = cm->get_CodeFragment(4);
252  cm->require_linking(this, cf);
253  return;
254  }
255  s4 offset = cm->get_offset(idx);
256  if (offset == 0) {
257  LOG2("JumpInst::emit: jump to the next instruction -> can be omitted ("
258  << this << " to " << *MBB << ")" << nl);
259  return;
260  }
261  CodeFragment cf = cm->get_CodeFragment(4);
262  Emitter em;
263  em.b(offset);
264  em.emit(cf);
265 }
266 
267 void JumpInst::link(CodeFragment& cf) const {
269  CodeSegment &cs = cf.get_Segment();
270  CodeSegment::IdxTy idx = cs.get_index(CSLabel(mbb));
271  LOG2("JumpInst::link BI: " << *mbb << " idx: " << idx.idx << " CF begin: "
272  << cf.get_begin().idx << " CF end: " << cf.get_end().idx << nl);
273  s4 offset = cs.get_CodeMemory().get_offset(idx, cf);
274  assert(offset != 0);
275 
276  Emitter em;
277  em.b(offset);
278  em.emit(cf);
279 }
280 
282  // update jump target (might have changed)
284  // emit else jump (if needed)
285  jump.emit(cm);
286 
288  CodeSegment &cs = cm->get_CodeSegment();
289  CodeSegment::IdxTy idx = cs.get_index(CSLabel(mbb));
290  if (CodeSegment::is_invalid(idx)) {
291  LOG2("CondJumpInst::emit: target not yet known (" << this << " to "
292  << *mbb << ")" << nl);
293  // TODO: implement this
294  return;
295  }
296  s4 offset = cm->get_offset(idx);
297  if (offset == 0) {
298  // XXX fix me! remove empty blocks?
299  ABORT_MSG("Aarch64 ERROR","CondJump offset 0 oO!");
300  //return;
301  }
302  LOG2("found offset of " << *mbb << ": " << offset << nl);
303 
304  Emitter em;
305  em.bcond(cond.code, offset);
306  em.emit(cm);
307 }
308 
310  assert(false);
311  // TODO: implement me
312 }
313 
314 void EnterInst::emit(CodeMemory* cm) const {
315  // TODO: handle differently for leaf methods
316  Emitter em;
317 
318  // stp x29, x30, [sp, -16]!
319  u4 stp = 0xa9800000 | 0x1d | (0x1f << 5) | (0x1e << 10);
320  s2 imm = - 2;
321  stp |= (imm & 0x7f) << 15;
322  em.emitRaw(stp);
323 
324  // mov x29, sp
325  u4 mov = 0x91000000 | 0x1d | (0x1f << 5);
326  em.emitRaw(mov);
327 
328  // sub sp, sp, size
329  if (framesize - 16 > 0)
330  em.sub(Reg::XSP, Reg::XSP, framesize - 16);
331 
332  em.emit(cm);
333 }
334 
335 void LeaveInst::emit(CodeMemory* cm) const {
336  // TODO: handle differently for leaf methods
337  Emitter em;
338 
339  // mov sp, x29
340  em.add(Reg::XSP, Reg::XFP, 0);
341 
342  // ldp x29, x30, [sp] 16
343  u4 ldp = 0xa8c00000 | 0x1d | (0x1f << 5) | (0x1e << 10);
344  s2 imm = 2;
345  ldp |= (imm & 0x7f) << 15;
346  em.emitRaw(ldp);
347 
348  em.emit(cm);
349 }
350 
351 void RetInst::emit(CodeMemory* cm) const {
352  u4 ret = 0xd65f0000 | (0x1e << 5);
353  emitRaw(cm, ret);
354 }
355 
356 void FcvtInst::emit(Emitter& em) const {
357  em.fcvt(reg_res(), reg_from());
358 }
359 
360 void IntToFpInst::emit(Emitter& em) const {
361  em.scvtf(reg_res(), reg_from());
362 }
363 
364 void IntToLongInst::emit(Emitter& em) const {
365  em.sxtw(reg_res(), reg_op(0));
366 }
367 
368 void IntToCharInst::emit(Emitter& em) const {
369  em.uxth(reg_res(), reg_op(0));
370 }
371 
373  em.sxtb(reg_res(), reg_op(0));
374 }
375 
377  em.sxth(reg_res(), reg_op(0));
378 }
379 
380 void LongToIntInst::emit(Emitter& em) const {
381  em.sxth(reg_res(), reg_op(0));
382 }
383 
384 void PatchInst::emit(CodeMemory* CM) const {
385  UNIMPLEMENTED_MSG("aarch64: PatchInst::emit");
386  #if 0
388  code[0] = 0x0f;
389  code[1] = 0x0b;
390  emit_nop(code + 2, code.size() - 2);
391  CM->require_linking(this,code);
392  #endif
393 }
394 
395 void PatchInst::link(CodeFragment &CF) const {
396  UNIMPLEMENTED_MSG("aarch64: PatchInst::emit");
397  #if 0
398  CodeMemory &CM = CF.get_Segment().get_CodeMemory();
400  LOG2(this << " link: reposition: " << patcher->get_mpc() << nl);
401  #endif
402 }
403 
405  // Load DSeg address
406  CodeFragment code = cm->get_CodeFragment(4);
407  cm->require_linking(this, code);
408 }
409 
411  CodeMemory &cm = cf.get_Segment().get_CodeMemory();
412  s4 offset = cm.get_offset(data_index, cf);
413  #if 0
414  LOG2(this << " offset: " << offset << " data index: " << data_index.idx
415  << " CF end index: " << cf.get_end().idx << nl);
416  LOG2("dseg->size " << cm.get_DataSegment().size() << " cseg->size "
417  << cm.get_CodeSegment().size() << nl);
418  #endif
419  assert(offset != 0);
420 
421  Emitter em;
422  em.ldr(reg_res(), offset);
423  em.emit(cf);
424 }
425 
426 void CallInst::emit(Emitter& em) const {
427  em.blr(reg_op(0));
428 }
429 
430 void TrapInst::emit(Emitter& em) const {
431  em.trap(reg_op(0), trap);
432 }
433 
434 void CondTrapInst::emit(Emitter& em) const {
435  em.bcond(cond.code, 2);
436  em.trap(reg_op(0), trap);
437 }
438 
439 } // end namespace aarch64
440 } // end namespace compiler2
441 } // end namespace jit
442 } // end namespace cacao
443 
444 
445 /*
446  * These are local overrides for various environment variables in Emacs.
447  * Please do not remove this and leave it at the end of the file, where
448  * Emacs will automagically detect them.
449  * ---------------------------------------------------------------------
450  * Local variables:
451  * mode: c++
452  * indent-tabs-mode: t
453  * c-basic-offset: 4
454  * tab-width: 4
455  * End:
456  * vim:noexpandtab:sw=4:ts=4:
457  */
virtual void emit(Emitter &em) const
virtual void emit(Emitter &em) const
void movk(const Reg &rd, u2 imm, u1 shift=0)
CodeFragment get_CodeFragment(std::size_t size)
get a code fragment
Definition: CodeMemory.cpp:79
virtual void emit(CodeMemory *cm) const
emit machine code
CodeFragment get_aligned_CodeFragment(std::size_t size)
get an aligned code fragment
Definition: CodeMemory.cpp:83
virtual void emit(Emitter &em) const
virtual void emit(Emitter &em) const
virtual void link(CodeFragment &cf) const
link machine code
void fneg(const Reg &rd, const Reg &rn)
virtual void emit(CodeMemory *cm) const
emit machine code
void ldur(const Reg &rt, const Reg &rn, s2 imm=0)
virtual void emit(Emitter &em) const
void fmul(const Reg &rd, const Reg &rn, const Reg &rm)
s4 trap
A trap number as defined in aarch64/md-trap.hpp.
u2 op
Definition: disass.cpp:129
A basic block of (scheduled) machine instructions.
void csel(const Reg &rd, const Reg &rn, const Reg &rm, Cond::COND cond)
virtual void emit(CodeMemory *cm) const
emit machine code
void fadd(const Reg &rd, const Reg &rn, const Reg &rm)
void sxth(const Reg &rd, const Reg &rn)
void fcvt(const Reg &rd, const Reg &rn)
void add(const Reg &rd, const Reg &rn, s2 imm)
void msub(const Reg &rd, const Reg &rn, const Reg &rm, const Reg &ra)
void neg(const Reg &rd, const Reg &rm)
virtual void link(CodeFragment &cf) const
link machine code
uint8_t u1
Definition: types.hpp:40
void str(const Reg &rt, const Reg &rn, s2 imm=0)
virtual void emit(Emitter &em) const
virtual void link(CodeFragment &CF) const
link machine code
virtual void emit(Emitter &em) const
std::size_t size() const
get size
Definition: Segment.hpp:153
int64_t s8
Definition: types.hpp:48
virtual void emit(Emitter &em) const
virtual uintptr_t get_mpc() const =0
get the absolute position in code segment
const CodeSegment & get_CodeSegment() const
get CodeSegment
Definition: CodeMemory.hpp:65
virtual void emit(Emitter &em) const
void require_linking(const MachineInstruction *, CodeFragment CF)
Add a MachineInstruction that require linking.
Definition: CodeMemory.cpp:56
MachineBasicBlock * successor_front() const
virtual void emit(Emitter &em) const
void sdiv(const Reg &rd, const Reg &rn, const Reg &rm)
s4 get_offset(CodeSegment::IdxTy to, CodeSegment::IdxTy from) const
Definition: CodeMemory.cpp:43
virtual void emit(Emitter &em) const
virtual void emit(Emitter &em) const
void andd(const Reg &rd, const Reg &rn, const Reg &rm)
Segment< Tag, RefCategory > * get_Segment() const
Get containing segment.
Definition: Segment.hpp:328
Immediate * cast_to< Immediate >(MachineOperand *op)
virtual void emit(Emitter &em) const
void fdiv(const Reg &rd, const Reg &rn, const Reg &rm)
uint16_t u2
Definition: types.hpp:43
CodeMemory * get_CodeMemory() const
Get containing CodeMemory.
Definition: Segment.hpp:145
uint64_t u8
Definition: types.hpp:49
IdxTy get_end() const
Get the index of the first element after the reference.
Definition: Segment.hpp:356
virtual void emit(CodeMemory *cm) const
emit machine code
static void emitRaw(CodeMemory *cm, u4 inst)
void sub(const Reg &rd, const Reg &rn, s2 imm)
virtual void emit(Emitter &em) const
#define UNIMPLEMENTED_MSG(EXPR_LONG)
Definition: logging.hpp:145
void scvtf(const Reg &rd, const Reg &rn)
virtual void reposition(intptr_t base)=0
reposition to another base
std::size_t size() const
size of the reference
Definition: Segment.hpp:392
#define LOG2(STMT)
Definition: logging.hpp:93
PointerTag< CodeSegmentType, const MachineBasicBlock, LabelID > CSLabel
Definition: CodeSegment.hpp:42
int32_t s4
Definition: types.hpp:45
void stur(const Reg &rt, const Reg &rn, s2 imm=0)
virtual void emit(Emitter &em) const
void set_target(MachineBasicBlock *target)
virtual void emit(Emitter &em) const
virtual void emit(CodeMemory *cm) const
emit machine code
IdxTy get_index(Tag2 tag) const
get the index of a tag
Definition: Segment.hpp:254
void fmov(const Reg &rd, const Reg &rn)
virtual void link(CodeFragment &cf) const
link machine code
virtual void emit(Emitter &em) const
virtual void emit(CodeMemory *cm) const
emit machine code
virtual void emit(Emitter &em) const
uint32_t u4
Definition: types.hpp:46
void mul(const Reg &rd, const Reg &rn, const Reg &rm)
void sxtb(const Reg &rd, const Reg &rn)
void sxtw(const Reg &xd, const Reg &wn)
virtual void emit(Emitter &em) const
void fcmp(const Reg &rn, const Reg &rm)
void emit_nop(codegendata *cd)
Definition: emit-asm.hpp:603
void fsub(const Reg &rd, const Reg &rn, const Reg &rm)
void mov(const Reg &rd, const Reg &rm)
s4 trap
A trap number as defined in aarch64/md-trap.hpp.
void ldr(const Reg &rt, s4 offset)
void trap(const Reg &rd, int type)
virtual void emit(Emitter &em) const
int16_t s2
Definition: types.hpp:42
Segment reference.
Definition: Segment.hpp:44
IdxTy get_begin() const
Get the index of the first element.
Definition: Segment.hpp:343
#define str(x)
virtual void emit(Emitter &em) const
virtual void emit(CodeMemory *cm) const
emit machine code
void uxth(const Reg &wd, const Reg &wn)
#define ABORT_MSG(EXPR_SHORT, EXPR_LONG)
Definition: logging.hpp:133
Nl nl
Definition: OStream.cpp:56
void ubfx(const Reg &wd, const Reg &wn)
const DataSegment & get_DataSegment() const
get DataSegment
Definition: CodeMemory.hpp:69