aboutsummaryrefslogtreecommitdiff
path: root/src/cpu/aarch64/vm/aarch64_call.cpp
blob: 7e1738c31750e3476b5e017e93d222cfc6b703fb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/*
 * Copyright (c) 2013, Red Hat Inc.
 * All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */

#ifdef BUILTIN_SIM

#include <stdio.h>
#include <sys/types.h>
#include "asm/macroAssembler.hpp"
#include "asm/macroAssembler.inline.hpp"
#include "runtime/sharedRuntime.hpp"
#include "../../../../../../simulator/cpustate.hpp"
#include "../../../../../../simulator/simulator.hpp"

/*
 * a routine to initialise and enter ARM simulator execution when
 * calling into ARM code from x86 code.
 *
 * we maintain a simulator per-thread and provide it with 8 Mb of
 * stack space
 */
#define SIM_STACK_SIZE (1024 * 1024) // in units of u_int64_t

extern "C" u_int64_t get_alt_stack()
{
  return AArch64Simulator::altStack();
}

extern "C" void setup_arm_sim(void *sp, u_int64_t calltype)
{
  // n.b. this function runs on the simulator stack so as to avoid
  // simulator frames appearing in between VM x86 and ARM frames. note
  // that arfgument sp points to the old (VM) stack from which the
  // call into the sim was made. The stack switch and entry into this
  // routine is handled by x86 prolog code planted in the head of the
  // ARM code buffer which the sim is about to start executing (see
  // aarch64_linkage.S).
  //
  // The first ARM instruction in the buffer is identified by fnptr
  // stored at the top of the old stack. x86 register contents precede
  // fnptr. preceding that are the fp and return address of the VM
  // caller into ARM code. any extra, non-register arguments passed to
  // the linkage routine precede the fp (this is as per any normal x86
  // call wirth extra args).
  //
  // note that the sim creates Java frames on the Java stack just
  // above sp (i.e. directly above fnptr). it sets the sim FP register
  // to the pushed fp for the caller effectively eliding the register
  // data saved by the linkage routine.
  //
  // x86 register call arguments are loaded from the stack into ARM
  // call registers. if extra arguments occur preceding the x86
  // caller's fp then they are copied either into extra ARM registers
  // (ARM has 8 rather than 6 gp call registers) or up the stack
  // beyond the saved x86 registers so that they immediately precede
  // the ARM frame where the ARM calling convention expects them to
  // be.
  //
  // n.b. the number of register/stack values passed to the ARM code
  // is determined by calltype
  //
  // +--------+  
  // | fnptr  |  <--- argument sp points here
  // +--------+  |
  // | rax    |  | return slot if we need to return a value
  // +--------+  |
  // | rdi    |  increasing
  // +--------+  address
  // | rsi    |  |
  // +--------+  V
  // | rdx    |
  // +--------+
  // | rcx    |
  // +--------+
  // | r8     |
  // +--------+
  // | r9     |
  // +--------+
  // | xmm0   |
  // +--------+
  // | xmm1   |
  // +--------+
  // | xmm2   |
  // +--------+
  // | xmm3   |
  // +--------+
  // | xmm4   |
  // +--------+
  // | xmm5   |
  // +--------+
  // | xmm6   |
  // +--------+
  // | xmm7   |
  // +--------+
  // | fp     |
  // +--------+
  // | caller |
  // | ret ip |
  // +--------+
  // | arg0   | <-- any extra call args start here
  // +--------+     offset = 18 * wordSize
  // | . . .  |     (i.e. 1 * calladdr + 1 * rax  + 6 * gp call regs
  //                      + 8 * fp call regs + 2 * frame words)
  //
  // we use a unique sim/stack per thread
  const int cursor2_offset = 18;
  const int fp_offset = 16;
  u_int64_t *cursor = (u_int64_t *)sp;
  u_int64_t *cursor2 = ((u_int64_t *)sp) + cursor2_offset;
  u_int64_t *fp = ((u_int64_t *)sp) + fp_offset;
  int gp_arg_count = calltype & 0xf;
  int fp_arg_count = (calltype >> 4) & 0xf;
  int return_type = (calltype >> 8) & 0x3;
  AArch64Simulator *sim = AArch64Simulator::get_current(UseSimulatorCache, DisableBCCheck);
  // save previous cpu state in case this is a recursive entry
  CPUState saveState = sim->getCPUState();
  // set up initial sim pc, sp and fp registers
  sim->init(*cursor++, (u_int64_t)sp, (u_int64_t)fp);
  u_int64_t *return_slot = cursor++;

  // if we need to pass the sim extra args on the stack then bump
  // the stack pointer now
  u_int64_t *cursor3 = (u_int64_t *)sim->getCPUState().xreg(SP, 1);
  if (gp_arg_count > 8) {
    cursor3 -= gp_arg_count - 8;
  }
  if (fp_arg_count > 8) {
    cursor3 -= fp_arg_count - 8;
  }
  sim->getCPUState().xreg(SP, 1) = (u_int64_t)(cursor3++);

  for (int i = 0; i < gp_arg_count; i++) {
    if (i < 6) {
      // copy saved register to sim register
      GReg reg = (GReg)i;
      sim->getCPUState().xreg(reg, 0) = *cursor++;
    } else if (i < 8) {
      // copy extra int arg to sim register
      GReg reg = (GReg)i;
      sim->getCPUState().xreg(reg, 0) = *cursor2++;
    } else {
      // copy extra fp arg to sim stack      
      *cursor3++ = *cursor2++;
    }
  }
  for (int i = 0; i < fp_arg_count; i++) {
    if (i < 8) {
      // copy saved register to sim register
      GReg reg = (GReg)i;
      sim->getCPUState().xreg(reg, 0) = *cursor++;
    } else {
      // copy extra arg to sim stack      
      *cursor3++ = *cursor2++;
    }
  }
  AArch64Simulator::status_t return_status = sim->run();
  if (return_status != AArch64Simulator::STATUS_RETURN){
    sim->simPrint0();
    fatal("invalid status returned from simulator.run()\n");
  }
  switch (return_type) {
  case MacroAssembler::ret_type_void:
  default:
    break;
  case MacroAssembler::ret_type_integral:
  // this overwrites the saved r0
    *return_slot = sim->getCPUState().xreg(R0, 0);
    break;
  case MacroAssembler::ret_type_float:
    *(float *)return_slot = sim->getCPUState().sreg(V0);
    break;
  case MacroAssembler::ret_type_double:
    *(double *)return_slot = sim->getCPUState().dreg(V0);
    break;
  }
  // restore incoimng cpu state
  sim->getCPUState() = saveState;
}

#endif