blob: 036da2e2b2d246369e9e93d0a89123f14f5bfa5b [file] [log] [blame]
Matteo Franchin43ec8732014-03-31 15:00:14 +01001/*
2 * Copyright (C) 2011 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/* This file contains codegen for the Thumb2 ISA. */
18
Matteo Franchin43ec8732014-03-31 15:00:14 +010019#include "codegen_arm64.h"
Andreas Gampe0b9203e2015-01-22 20:39:27 -080020
21#include "arm64_lir.h"
Mathieu Chartiere401d142015-04-22 13:56:20 -070022#include "art_method.h"
Andreas Gampe0b9203e2015-01-22 20:39:27 -080023#include "base/logging.h"
24#include "dex/mir_graph.h"
Jeff Hao848f70a2014-01-15 13:49:50 -080025#include "dex/quick/dex_file_to_method_inliner_map.h"
Matteo Franchin43ec8732014-03-31 15:00:14 +010026#include "dex/quick/mir_to_lir-inl.h"
Andreas Gampe0b9203e2015-01-22 20:39:27 -080027#include "driver/compiler_driver.h"
Vladimir Marko20f85592015-03-19 10:07:02 +000028#include "driver/compiler_options.h"
Ian Rogers576ca0c2014-06-06 15:58:22 -070029#include "gc/accounting/card_table.h"
Matteo Franchin43ec8732014-03-31 15:00:14 +010030#include "entrypoints/quick/quick_entrypoints.h"
Vladimir Marko7c2ad5a2014-09-24 12:42:55 +010031#include "mirror/object_array-inl.h"
Vladimir Marko20f85592015-03-19 10:07:02 +000032#include "utils/dex_cache_arrays_layout-inl.h"
Matteo Franchin43ec8732014-03-31 15:00:14 +010033
34namespace art {
35
36/*
37 * The sparse table in the literal pool is an array of <key,displacement>
Matteo Franchine45fb9e2014-05-06 10:10:30 +010038 * pairs. For each set, we'll load them as a pair using ldp.
Matteo Franchin43ec8732014-03-31 15:00:14 +010039 * The test loop will look something like:
40 *
41 * adr r_base, <table>
Matteo Franchine45fb9e2014-05-06 10:10:30 +010042 * ldr r_val, [rA64_SP, v_reg_off]
Matteo Franchin43ec8732014-03-31 15:00:14 +010043 * mov r_idx, #table_size
Matteo Franchine45fb9e2014-05-06 10:10:30 +010044 * loop:
45 * cbz r_idx, quit
46 * ldp r_key, r_disp, [r_base], #8
Matteo Franchin43ec8732014-03-31 15:00:14 +010047 * sub r_idx, #1
48 * cmp r_val, r_key
Matteo Franchine45fb9e2014-05-06 10:10:30 +010049 * b.ne loop
50 * adr r_base, #0 ; This is the instruction from which we compute displacements
51 * add r_base, r_disp
52 * br r_base
53 * quit:
Matteo Franchin43ec8732014-03-31 15:00:14 +010054 */
Andreas Gampe48971b32014-08-06 10:09:01 -070055void Arm64Mir2Lir::GenLargeSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src) {
Razvan A Lupusoru8d0d03e2014-06-06 17:04:52 -070056 const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
Matteo Franchin43ec8732014-03-31 15:00:14 +010057 // Add the table to the list - we'll process it later
58 SwitchTable *tab_rec =
59 static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), kArenaAllocData));
Chao-ying Fu72f53af2014-11-11 16:48:40 -080060 tab_rec->switch_mir = mir;
Matteo Franchin43ec8732014-03-31 15:00:14 +010061 tab_rec->table = table;
62 tab_rec->vaddr = current_dalvik_offset_;
63 uint32_t size = table[1];
Vladimir Markoe39c54e2014-09-22 14:50:02 +010064 switch_tables_.push_back(tab_rec);
Matteo Franchin43ec8732014-03-31 15:00:14 +010065
66 // Get the switch value
67 rl_src = LoadValue(rl_src, kCoreReg);
Matteo Franchin5acc8b02014-06-05 15:10:35 +010068 RegStorage r_base = AllocTempWide();
Matteo Franchine45fb9e2014-05-06 10:10:30 +010069 // Allocate key and disp temps.
Matteo Franchin43ec8732014-03-31 15:00:14 +010070 RegStorage r_key = AllocTemp();
71 RegStorage r_disp = AllocTemp();
Matteo Franchin43ec8732014-03-31 15:00:14 +010072 // Materialize a pointer to the switch table
Matteo Franchine45fb9e2014-05-06 10:10:30 +010073 NewLIR3(kA64Adr2xd, r_base.GetReg(), 0, WrapPointer(tab_rec));
Matteo Franchin43ec8732014-03-31 15:00:14 +010074 // Set up r_idx
75 RegStorage r_idx = AllocTemp();
76 LoadConstant(r_idx, size);
Matteo Franchine45fb9e2014-05-06 10:10:30 +010077
78 // Entry of loop.
79 LIR* loop_entry = NewLIR0(kPseudoTargetLabel);
80 LIR* branch_out = NewLIR2(kA64Cbz2rt, r_idx.GetReg(), 0);
81
82 // Load next key/disp.
83 NewLIR4(kA64LdpPost4rrXD, r_key.GetReg(), r_disp.GetReg(), r_base.GetReg(), 2);
84 OpRegRegImm(kOpSub, r_idx, r_idx, 1);
85
86 // Go to next case, if key does not match.
Matteo Franchin43ec8732014-03-31 15:00:14 +010087 OpRegReg(kOpCmp, r_key, rl_src.reg);
Matteo Franchine45fb9e2014-05-06 10:10:30 +010088 OpCondBranch(kCondNe, loop_entry);
89
90 // Key does match: branch to case label.
91 LIR* switch_label = NewLIR3(kA64Adr2xd, r_base.GetReg(), 0, -1);
92 tab_rec->anchor = switch_label;
93
94 // Add displacement to base branch address and go!
Andreas Gampe47b31aa2014-06-19 01:10:07 -070095 OpRegRegRegExtend(kOpAdd, r_base, r_base, As64BitReg(r_disp), kA64Sxtw, 0U);
Matteo Franchine45fb9e2014-05-06 10:10:30 +010096 NewLIR1(kA64Br1x, r_base.GetReg());
97
98 // Loop exit label.
99 LIR* loop_exit = NewLIR0(kPseudoTargetLabel);
100 branch_out->target = loop_exit;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100101}
102
103
Andreas Gampe48971b32014-08-06 10:09:01 -0700104void Arm64Mir2Lir::GenLargePackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src) {
Razvan A Lupusoru8d0d03e2014-06-06 17:04:52 -0700105 const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100106 // Add the table to the list - we'll process it later
107 SwitchTable *tab_rec =
108 static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), kArenaAllocData));
Chao-ying Fu72f53af2014-11-11 16:48:40 -0800109 tab_rec->switch_mir = mir;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100110 tab_rec->table = table;
111 tab_rec->vaddr = current_dalvik_offset_;
112 uint32_t size = table[1];
Vladimir Markoe39c54e2014-09-22 14:50:02 +0100113 switch_tables_.push_back(tab_rec);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100114
115 // Get the switch value
116 rl_src = LoadValue(rl_src, kCoreReg);
Matteo Franchin5acc8b02014-06-05 15:10:35 +0100117 RegStorage table_base = AllocTempWide();
Matteo Franchin43ec8732014-03-31 15:00:14 +0100118 // Materialize a pointer to the switch table
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100119 NewLIR3(kA64Adr2xd, table_base.GetReg(), 0, WrapPointer(tab_rec));
Matteo Franchin43ec8732014-03-31 15:00:14 +0100120 int low_key = s4FromSwitchData(&table[2]);
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100121 RegStorage key_reg;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100122 // Remove the bias, if necessary
123 if (low_key == 0) {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100124 key_reg = rl_src.reg;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100125 } else {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100126 key_reg = AllocTemp();
127 OpRegRegImm(kOpSub, key_reg, rl_src.reg, low_key);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100128 }
129 // Bounds check - if < 0 or >= size continue following switch
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100130 OpRegImm(kOpCmp, key_reg, size - 1);
Mathieu Chartier2cebb242015-04-21 16:50:40 -0700131 LIR* branch_over = OpCondBranch(kCondHi, nullptr);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100132
133 // Load the displacement from the switch table
134 RegStorage disp_reg = AllocTemp();
Andreas Gampe4b537a82014-06-30 22:24:53 -0700135 LoadBaseIndexed(table_base, As64BitReg(key_reg), disp_reg, 2, k32);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100136
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100137 // Get base branch address.
Matteo Franchin5acc8b02014-06-05 15:10:35 +0100138 RegStorage branch_reg = AllocTempWide();
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100139 LIR* switch_label = NewLIR3(kA64Adr2xd, branch_reg.GetReg(), 0, -1);
140 tab_rec->anchor = switch_label;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100141
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100142 // Add displacement to base branch address and go!
Andreas Gampe47b31aa2014-06-19 01:10:07 -0700143 OpRegRegRegExtend(kOpAdd, branch_reg, branch_reg, As64BitReg(disp_reg), kA64Sxtw, 0U);
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100144 NewLIR1(kA64Br1x, branch_reg.GetReg());
145
146 // branch_over target here
Matteo Franchin43ec8732014-03-31 15:00:14 +0100147 LIR* target = NewLIR0(kPseudoTargetLabel);
148 branch_over->target = target;
149}
150
151/*
Matteo Franchin43ec8732014-03-31 15:00:14 +0100152 * Handle unlocked -> thin locked transition inline or else call out to quick entrypoint. For more
153 * details see monitor.cc.
154 */
155void Arm64Mir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) {
Zheng Xuc8304302014-05-15 17:21:01 +0100156 // x0/w0 = object
157 // w1 = thin lock thread id
158 // x2 = address of lock word
159 // w3 = lock word / store failure
160 // TUNING: How much performance we get when we inline this?
161 // Since we've already flush all register.
Matteo Franchin43ec8732014-03-31 15:00:14 +0100162 FlushAllRegs();
Andreas Gampeccc60262014-07-04 18:02:38 -0700163 LoadValueDirectFixed(rl_src, rs_x0); // = TargetReg(kArg0, kRef)
Matteo Franchin43ec8732014-03-31 15:00:14 +0100164 LockCallTemps(); // Prepare for explicit register usage
Zheng Xuc8304302014-05-15 17:21:01 +0100165 LIR* null_check_branch = nullptr;
166 if ((opt_flags & MIR_IGNORE_NULL_CHECK) && !(cu_->disable_opt & (1 << kNullCheckElimination))) {
167 null_check_branch = nullptr; // No null check.
Matteo Franchin43ec8732014-03-31 15:00:14 +0100168 } else {
Zheng Xuc8304302014-05-15 17:21:01 +0100169 // If the null-check fails its handled by the slow-path to reduce exception related meta-data.
Dave Allison69dfe512014-07-11 17:11:58 +0000170 if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) {
Mathieu Chartier2cebb242015-04-21 16:50:40 -0700171 null_check_branch = OpCmpImmBranch(kCondEq, rs_x0, 0, nullptr);
Zheng Xuc8304302014-05-15 17:21:01 +0100172 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100173 }
Zheng Xubaa7c882014-06-30 14:26:50 +0800174 Load32Disp(rs_xSELF, Thread::ThinLockIdOffset<8>().Int32Value(), rs_w1);
Zheng Xuc8304302014-05-15 17:21:01 +0100175 OpRegRegImm(kOpAdd, rs_x2, rs_x0, mirror::Object::MonitorOffset().Int32Value());
176 NewLIR2(kA64Ldxr2rX, rw3, rx2);
177 MarkPossibleNullPointerException(opt_flags);
Hiroshi Yamauchie15ea082015-02-09 17:11:42 -0800178 // Zero out the read barrier bits.
179 OpRegRegImm(kOpAnd, rs_w2, rs_w3, LockWord::kReadBarrierStateMaskShiftedToggled);
Mathieu Chartier2cebb242015-04-21 16:50:40 -0700180 LIR* not_unlocked_branch = OpCmpImmBranch(kCondNe, rs_w2, 0, nullptr);
Hiroshi Yamauchie15ea082015-02-09 17:11:42 -0800181 // w3 is zero except for the rb bits here. Copy the read barrier bits into w1.
182 OpRegRegReg(kOpOr, rs_w1, rs_w1, rs_w3);
183 OpRegRegImm(kOpAdd, rs_x2, rs_x0, mirror::Object::MonitorOffset().Int32Value());
Zheng Xuc8304302014-05-15 17:21:01 +0100184 NewLIR3(kA64Stxr3wrX, rw3, rw1, rx2);
Mathieu Chartier2cebb242015-04-21 16:50:40 -0700185 LIR* lock_success_branch = OpCmpImmBranch(kCondEq, rs_w3, 0, nullptr);
Zheng Xuc8304302014-05-15 17:21:01 +0100186
187 LIR* slow_path_target = NewLIR0(kPseudoTargetLabel);
188 not_unlocked_branch->target = slow_path_target;
189 if (null_check_branch != nullptr) {
190 null_check_branch->target = slow_path_target;
191 }
192 // TODO: move to a slow path.
193 // Go expensive route - artLockObjectFromCode(obj);
Zheng Xubaa7c882014-06-30 14:26:50 +0800194 LoadWordDisp(rs_xSELF, QUICK_ENTRYPOINT_OFFSET(8, pLockObject).Int32Value(), rs_xLR);
Zheng Xuc8304302014-05-15 17:21:01 +0100195 ClobberCallerSave();
Zheng Xubaa7c882014-06-30 14:26:50 +0800196 LIR* call_inst = OpReg(kOpBlx, rs_xLR);
Zheng Xuc8304302014-05-15 17:21:01 +0100197 MarkSafepointPC(call_inst);
198
199 LIR* success_target = NewLIR0(kPseudoTargetLabel);
200 lock_success_branch->target = success_target;
Hans Boehm48f5c472014-06-27 14:50:10 -0700201 GenMemBarrier(kLoadAny);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100202}
203
204/*
205 * Handle thin locked -> unlocked transition inline or else call out to quick entrypoint. For more
Zheng Xuc8304302014-05-15 17:21:01 +0100206 * details see monitor.cc. Note the code below doesn't use ldxr/stxr as the code holds the lock
Matteo Franchin43ec8732014-03-31 15:00:14 +0100207 * and can only give away ownership if its suspended.
208 */
209void Arm64Mir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) {
Zheng Xuc8304302014-05-15 17:21:01 +0100210 // x0/w0 = object
211 // w1 = thin lock thread id
212 // w2 = lock word
213 // TUNING: How much performance we get when we inline this?
214 // Since we've already flush all register.
Matteo Franchin43ec8732014-03-31 15:00:14 +0100215 FlushAllRegs();
Andreas Gampe4b537a82014-06-30 22:24:53 -0700216 LoadValueDirectFixed(rl_src, rs_x0); // Get obj
Matteo Franchin43ec8732014-03-31 15:00:14 +0100217 LockCallTemps(); // Prepare for explicit register usage
218 LIR* null_check_branch = nullptr;
Zheng Xuc8304302014-05-15 17:21:01 +0100219 if ((opt_flags & MIR_IGNORE_NULL_CHECK) && !(cu_->disable_opt & (1 << kNullCheckElimination))) {
220 null_check_branch = nullptr; // No null check.
Matteo Franchin43ec8732014-03-31 15:00:14 +0100221 } else {
Zheng Xuc8304302014-05-15 17:21:01 +0100222 // If the null-check fails its handled by the slow-path to reduce exception related meta-data.
Dave Allison69dfe512014-07-11 17:11:58 +0000223 if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) {
Mathieu Chartier2cebb242015-04-21 16:50:40 -0700224 null_check_branch = OpCmpImmBranch(kCondEq, rs_x0, 0, nullptr);
Zheng Xuc8304302014-05-15 17:21:01 +0100225 }
Matteo Franchin43ec8732014-03-31 15:00:14 +0100226 }
Zheng Xubaa7c882014-06-30 14:26:50 +0800227 Load32Disp(rs_xSELF, Thread::ThinLockIdOffset<8>().Int32Value(), rs_w1);
Hiroshi Yamauchie15ea082015-02-09 17:11:42 -0800228 if (!kUseReadBarrier) {
229 Load32Disp(rs_x0, mirror::Object::MonitorOffset().Int32Value(), rs_w2);
230 } else {
231 OpRegRegImm(kOpAdd, rs_x3, rs_x0, mirror::Object::MonitorOffset().Int32Value());
232 NewLIR2(kA64Ldxr2rX, rw2, rx3);
233 }
Zheng Xuc8304302014-05-15 17:21:01 +0100234 MarkPossibleNullPointerException(opt_flags);
Hiroshi Yamauchie15ea082015-02-09 17:11:42 -0800235 // Zero out the read barrier bits.
236 OpRegRegImm(kOpAnd, rs_w3, rs_w2, LockWord::kReadBarrierStateMaskShiftedToggled);
237 // Zero out except the read barrier bits.
238 OpRegRegImm(kOpAnd, rs_w2, rs_w2, LockWord::kReadBarrierStateMaskShifted);
Mathieu Chartier2cebb242015-04-21 16:50:40 -0700239 LIR* slow_unlock_branch = OpCmpBranch(kCondNe, rs_w3, rs_w1, nullptr);
Hans Boehm48f5c472014-06-27 14:50:10 -0700240 GenMemBarrier(kAnyStore);
Hiroshi Yamauchie15ea082015-02-09 17:11:42 -0800241 LIR* unlock_success_branch;
242 if (!kUseReadBarrier) {
243 Store32Disp(rs_x0, mirror::Object::MonitorOffset().Int32Value(), rs_w2);
Mathieu Chartier2cebb242015-04-21 16:50:40 -0700244 unlock_success_branch = OpUnconditionalBranch(nullptr);
Hiroshi Yamauchie15ea082015-02-09 17:11:42 -0800245 } else {
246 OpRegRegImm(kOpAdd, rs_x3, rs_x0, mirror::Object::MonitorOffset().Int32Value());
247 NewLIR3(kA64Stxr3wrX, rw1, rw2, rx3);
Mathieu Chartier2cebb242015-04-21 16:50:40 -0700248 unlock_success_branch = OpCmpImmBranch(kCondEq, rs_w1, 0, nullptr);
Hiroshi Yamauchie15ea082015-02-09 17:11:42 -0800249 }
Zheng Xuc8304302014-05-15 17:21:01 +0100250 LIR* slow_path_target = NewLIR0(kPseudoTargetLabel);
251 slow_unlock_branch->target = slow_path_target;
252 if (null_check_branch != nullptr) {
253 null_check_branch->target = slow_path_target;
254 }
255 // TODO: move to a slow path.
256 // Go expensive route - artUnlockObjectFromCode(obj);
Zheng Xubaa7c882014-06-30 14:26:50 +0800257 LoadWordDisp(rs_xSELF, QUICK_ENTRYPOINT_OFFSET(8, pUnlockObject).Int32Value(), rs_xLR);
Zheng Xuc8304302014-05-15 17:21:01 +0100258 ClobberCallerSave();
Zheng Xubaa7c882014-06-30 14:26:50 +0800259 LIR* call_inst = OpReg(kOpBlx, rs_xLR);
Zheng Xuc8304302014-05-15 17:21:01 +0100260 MarkSafepointPC(call_inst);
261
262 LIR* success_target = NewLIR0(kPseudoTargetLabel);
263 unlock_success_branch->target = success_target;
Matteo Franchin43ec8732014-03-31 15:00:14 +0100264}
265
266void Arm64Mir2Lir::GenMoveException(RegLocation rl_dest) {
Andreas Gampe2f244e92014-05-08 03:35:25 -0700267 int ex_offset = Thread::ExceptionOffset<8>().Int32Value();
buzbeea0cd2d72014-06-01 09:33:49 -0700268 RegLocation rl_result = EvalLoc(rl_dest, kRefReg, true);
Zheng Xubaa7c882014-06-30 14:26:50 +0800269 LoadRefDisp(rs_xSELF, ex_offset, rl_result.reg, kNotVolatile);
270 StoreRefDisp(rs_xSELF, ex_offset, rs_xzr, kNotVolatile);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100271 StoreValue(rl_dest, rl_result);
272}
273
Vladimir Markobf535be2014-11-19 18:52:35 +0000274void Arm64Mir2Lir::UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) {
Matteo Franchinfd2e2912014-06-06 10:09:56 +0100275 RegStorage reg_card_base = AllocTempWide();
Andreas Gampe4b537a82014-06-30 22:24:53 -0700276 RegStorage reg_card_no = AllocTempWide(); // Needs to be wide as addr is ref=64b
Zheng Xubaa7c882014-06-30 14:26:50 +0800277 LoadWordDisp(rs_xSELF, Thread::CardTableOffset<8>().Int32Value(), reg_card_base);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100278 OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift);
Matteo Franchinfd2e2912014-06-06 10:09:56 +0100279 // TODO(Arm64): generate "strb wB, [xB, wC, uxtw]" rather than "strb wB, [xB, xC]"?
Andreas Gampe4b537a82014-06-30 22:24:53 -0700280 StoreBaseIndexed(reg_card_base, reg_card_no, As32BitReg(reg_card_base),
Matteo Franchinfd2e2912014-06-06 10:09:56 +0100281 0, kUnsignedByte);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100282 FreeTemp(reg_card_base);
283 FreeTemp(reg_card_no);
284}
285
David Srbecky1109fb32015-04-07 20:21:06 +0100286static dwarf::Reg DwarfCoreReg(int num) {
287 return dwarf::Reg::Arm64Core(num);
288}
289
Matteo Franchin43ec8732014-03-31 15:00:14 +0100290void Arm64Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) {
David Srbecky1109fb32015-04-07 20:21:06 +0100291 DCHECK_EQ(cfi_.GetCurrentCFAOffset(), 0); // empty stack.
292
Matteo Franchin43ec8732014-03-31 15:00:14 +0100293 /*
Stuart Monteithf8ec48e2014-06-06 17:05:08 +0100294 * On entry, x0 to x7 are live. Let the register allocation
Matteo Franchin43ec8732014-03-31 15:00:14 +0100295 * mechanism know so it doesn't try to use any of them when
Stuart Monteithf8ec48e2014-06-06 17:05:08 +0100296 * expanding the frame or flushing.
297 * Reserve x8 & x9 for temporaries.
Matteo Franchin43ec8732014-03-31 15:00:14 +0100298 */
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100299 LockTemp(rs_x0);
300 LockTemp(rs_x1);
301 LockTemp(rs_x2);
302 LockTemp(rs_x3);
Stuart Monteithf8ec48e2014-06-06 17:05:08 +0100303 LockTemp(rs_x4);
304 LockTemp(rs_x5);
305 LockTemp(rs_x6);
306 LockTemp(rs_x7);
Zheng Xub551fdc2014-07-25 11:49:42 +0800307 LockTemp(rs_xIP0);
308 LockTemp(rs_xIP1);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100309
Stuart Monteithd5c78f42014-06-11 16:44:46 +0100310 /* TUNING:
311 * Use AllocTemp() and reuse LR if possible to give us the freedom on adjusting the number
312 * of temp registers.
313 */
314
Matteo Franchin43ec8732014-03-31 15:00:14 +0100315 /*
316 * We can safely skip the stack overflow check if we're
317 * a leaf *and* our frame size < fudge factor.
318 */
Matteo Franchin24314522014-11-12 18:06:14 +0000319 bool skip_overflow_check = mir_graph_->MethodIsLeaf() &&
320 !FrameNeedsStackCheck(frame_size_, kArm64);
Stuart Monteithf8ec48e2014-06-06 17:05:08 +0100321
Dave Allison648d7112014-07-25 16:15:27 -0700322 const size_t kStackOverflowReservedUsableBytes = GetStackOverflowReservedBytes(kArm64);
323 const bool large_frame = static_cast<size_t>(frame_size_) > kStackOverflowReservedUsableBytes;
324 bool generate_explicit_stack_overflow_check = large_frame ||
325 !cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks();
Stuart Monteithf8ec48e2014-06-06 17:05:08 +0100326 const int spill_count = num_core_spills_ + num_fp_spills_;
327 const int spill_size = (spill_count * kArm64PointerSize + 15) & ~0xf; // SP 16 byte alignment.
328 const int frame_size_without_spills = frame_size_ - spill_size;
329
Matteo Franchin43ec8732014-03-31 15:00:14 +0100330 if (!skip_overflow_check) {
Dave Allison648d7112014-07-25 16:15:27 -0700331 if (generate_explicit_stack_overflow_check) {
Andreas Gampef29ecd62014-07-29 00:35:00 -0700332 // Load stack limit
333 LoadWordDisp(rs_xSELF, Thread::StackEndOffset<8>().Int32Value(), rs_xIP1);
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100334 } else {
335 // Implicit stack overflow check.
336 // Generate a load from [sp, #-framesize]. If this is in the stack
337 // redzone we will get a segmentation fault.
Stuart Monteithd5c78f42014-06-11 16:44:46 +0100338
Andreas Gampef29ecd62014-07-29 00:35:00 -0700339 // TODO: If the frame size is small enough, is it possible to make this a pre-indexed load,
340 // so that we can avoid the following "sub sp" when spilling?
Stuart Monteithd5c78f42014-06-11 16:44:46 +0100341 OpRegRegImm(kOpSub, rs_x8, rs_sp, GetStackOverflowReservedBytes(kArm64));
Matteo Franchin24314522014-11-12 18:06:14 +0000342 Load32Disp(rs_x8, 0, rs_wzr);
Stuart Monteithd5c78f42014-06-11 16:44:46 +0100343 MarkPossibleStackOverflowException();
Matteo Franchin43ec8732014-03-31 15:00:14 +0100344 }
Stuart Monteithf8ec48e2014-06-06 17:05:08 +0100345 }
346
Andreas Gampef29ecd62014-07-29 00:35:00 -0700347 int spilled_already = 0;
348 if (spill_size > 0) {
349 spilled_already = SpillRegs(rs_sp, core_spill_mask_, fp_spill_mask_, frame_size_);
350 DCHECK(spill_size == spilled_already || frame_size_ == spilled_already);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100351 }
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100352
Andreas Gampef29ecd62014-07-29 00:35:00 -0700353 if (spilled_already != frame_size_) {
354 OpRegImm(kOpSub, rs_sp, frame_size_without_spills);
David Srbecky1109fb32015-04-07 20:21:06 +0100355 cfi_.AdjustCFAOffset(frame_size_without_spills);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100356 }
357
Stuart Monteithf8ec48e2014-06-06 17:05:08 +0100358 if (!skip_overflow_check) {
Dave Allison648d7112014-07-25 16:15:27 -0700359 if (generate_explicit_stack_overflow_check) {
Stuart Monteithf8ec48e2014-06-06 17:05:08 +0100360 class StackOverflowSlowPath: public LIRSlowPath {
361 public:
Vladimir Marko0b40ecf2015-03-20 12:08:03 +0000362 StackOverflowSlowPath(Mir2Lir* m2l, LIR* branch, size_t sp_displace)
363 : LIRSlowPath(m2l, branch),
Stuart Monteithf8ec48e2014-06-06 17:05:08 +0100364 sp_displace_(sp_displace) {
365 }
366 void Compile() OVERRIDE {
367 m2l_->ResetRegPool();
368 m2l_->ResetDefTracking();
369 GenerateTargetLabel(kPseudoThrowTarget);
370 // Unwinds stack.
Zheng Xubaa7c882014-06-30 14:26:50 +0800371 m2l_->OpRegImm(kOpAdd, rs_sp, sp_displace_);
David Srbecky1109fb32015-04-07 20:21:06 +0100372 m2l_->cfi().AdjustCFAOffset(-sp_displace_);
Stuart Monteithf8ec48e2014-06-06 17:05:08 +0100373 m2l_->ClobberCallerSave();
374 ThreadOffset<8> func_offset = QUICK_ENTRYPOINT_OFFSET(8, pThrowStackOverflow);
Zheng Xub551fdc2014-07-25 11:49:42 +0800375 m2l_->LockTemp(rs_xIP0);
376 m2l_->LoadWordDisp(rs_xSELF, func_offset.Int32Value(), rs_xIP0);
377 m2l_->NewLIR1(kA64Br1x, rs_xIP0.GetReg());
378 m2l_->FreeTemp(rs_xIP0);
David Srbecky1109fb32015-04-07 20:21:06 +0100379 m2l_->cfi().AdjustCFAOffset(sp_displace_);
Stuart Monteithf8ec48e2014-06-06 17:05:08 +0100380 }
381
382 private:
383 const size_t sp_displace_;
384 };
385
Andreas Gampef29ecd62014-07-29 00:35:00 -0700386 LIR* branch = OpCmpBranch(kCondUlt, rs_sp, rs_xIP1, nullptr);
387 AddSlowPath(new(arena_)StackOverflowSlowPath(this, branch, frame_size_));
Stuart Monteithf8ec48e2014-06-06 17:05:08 +0100388 }
Stuart Monteithf8ec48e2014-06-06 17:05:08 +0100389 }
390
Matteo Franchin43ec8732014-03-31 15:00:14 +0100391 FlushIns(ArgLocs, rl_method);
392
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100393 FreeTemp(rs_x0);
394 FreeTemp(rs_x1);
395 FreeTemp(rs_x2);
396 FreeTemp(rs_x3);
Stuart Monteithf8ec48e2014-06-06 17:05:08 +0100397 FreeTemp(rs_x4);
398 FreeTemp(rs_x5);
399 FreeTemp(rs_x6);
400 FreeTemp(rs_x7);
Zheng Xub551fdc2014-07-25 11:49:42 +0800401 FreeTemp(rs_xIP0);
402 FreeTemp(rs_xIP1);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100403}
404
405void Arm64Mir2Lir::GenExitSequence() {
David Srbecky1109fb32015-04-07 20:21:06 +0100406 cfi_.RememberState();
Matteo Franchin43ec8732014-03-31 15:00:14 +0100407 /*
408 * In the exit path, r0/r1 are live - make sure they aren't
409 * allocated by the register utilities as temps.
410 */
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100411 LockTemp(rs_x0);
412 LockTemp(rs_x1);
Andreas Gampef29ecd62014-07-29 00:35:00 -0700413 UnspillRegs(rs_sp, core_spill_mask_, fp_spill_mask_, frame_size_);
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100414
buzbeeb5860fb2014-06-21 15:31:01 -0700415 // Finally return.
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100416 NewLIR0(kA64Ret);
David Srbecky1109fb32015-04-07 20:21:06 +0100417 // The CFI should be restored for any code that follows the exit block.
418 cfi_.RestoreState();
419 cfi_.DefCFAOffset(frame_size_);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100420}
421
422void Arm64Mir2Lir::GenSpecialExitSequence() {
Matteo Franchine45fb9e2014-05-06 10:10:30 +0100423 NewLIR0(kA64Ret);
Matteo Franchin43ec8732014-03-31 15:00:14 +0100424}
425
Vladimir Marko6ce3eba2015-02-16 13:05:59 +0000426void Arm64Mir2Lir::GenSpecialEntryForSuspend() {
427 // Keep 16-byte stack alignment - push x0, i.e. ArtMethod*, lr.
428 core_spill_mask_ = (1u << rs_xLR.GetRegNum());
429 num_core_spills_ = 1u;
430 fp_spill_mask_ = 0u;
431 num_fp_spills_ = 0u;
432 frame_size_ = 16u;
433 core_vmap_table_.clear();
434 fp_vmap_table_.clear();
435 NewLIR4(WIDE(kA64StpPre4rrXD), rs_x0.GetReg(), rs_xLR.GetReg(), rs_sp.GetReg(), -frame_size_ / 8);
David Srbecky1109fb32015-04-07 20:21:06 +0100436 cfi_.AdjustCFAOffset(frame_size_);
437 // Do not generate CFI for scratch register x0.
438 cfi_.RelOffset(DwarfCoreReg(rxLR), 8);
Vladimir Marko6ce3eba2015-02-16 13:05:59 +0000439}
440
441void Arm64Mir2Lir::GenSpecialExitForSuspend() {
442 // Pop the frame. (ArtMethod* no longer needed but restore it anyway.)
443 NewLIR4(WIDE(kA64LdpPost4rrXD), rs_x0.GetReg(), rs_xLR.GetReg(), rs_sp.GetReg(), frame_size_ / 8);
David Srbecky1109fb32015-04-07 20:21:06 +0100444 cfi_.AdjustCFAOffset(-frame_size_);
445 cfi_.Restore(DwarfCoreReg(rxLR));
Vladimir Marko6ce3eba2015-02-16 13:05:59 +0000446}
447
Vladimir Marko7c2ad5a2014-09-24 12:42:55 +0100448static bool Arm64UseRelativeCall(CompilationUnit* cu, const MethodReference& target_method) {
Jeff Haoa0acc2d2015-01-27 11:22:04 -0800449 // Emit relative calls anywhere in the image or within a dex file otherwise.
Nicolas Geoffray4b018562015-11-05 08:47:52 +0000450 return cu->compiler_driver->IsImage() || cu->dex_file == target_method.dex_file;
Vladimir Marko7c2ad5a2014-09-24 12:42:55 +0100451}
452
453/*
454 * Bit of a hack here - in the absence of a real scheduling pass,
455 * emit the next instruction in static & direct invoke sequences.
456 */
Vladimir Marko20f85592015-03-19 10:07:02 +0000457int Arm64Mir2Lir::Arm64NextSDCallInsn(CompilationUnit* cu, CallInfo* info,
458 int state, const MethodReference& target_method,
Mathieu Chartiere401d142015-04-22 13:56:20 -0700459 uint32_t unused_idx ATTRIBUTE_UNUSED,
Vladimir Marko20f85592015-03-19 10:07:02 +0000460 uintptr_t direct_code, uintptr_t direct_method,
461 InvokeType type) {
Vladimir Marko20f85592015-03-19 10:07:02 +0000462 Arm64Mir2Lir* cg = static_cast<Arm64Mir2Lir*>(cu->cg.get());
Jeff Hao848f70a2014-01-15 13:49:50 -0800463 if (info->string_init_offset != 0) {
464 RegStorage arg0_ref = cg->TargetReg(kArg0, kRef);
465 switch (state) {
466 case 0: { // Grab target method* from thread pointer
Mathieu Chartiere401d142015-04-22 13:56:20 -0700467 cg->LoadWordDisp(rs_xSELF, info->string_init_offset, arg0_ref);
Jeff Hao848f70a2014-01-15 13:49:50 -0800468 break;
469 }
470 case 1: // Grab the code from the method*
471 if (direct_code == 0) {
472 // kInvokeTgt := arg0_ref->entrypoint
473 cg->LoadWordDisp(arg0_ref,
Mathieu Chartiere401d142015-04-22 13:56:20 -0700474 ArtMethod::EntryPointFromQuickCompiledCodeOffset(
Jeff Hao848f70a2014-01-15 13:49:50 -0800475 kArm64PointerSize).Int32Value(), cg->TargetPtrReg(kInvokeTgt));
476 }
477 break;
478 default:
479 return -1;
480 }
481 } else if (direct_code != 0 && direct_method != 0) {
Vladimir Marko7c2ad5a2014-09-24 12:42:55 +0100482 switch (state) {
483 case 0: // Get the current Method* [sets kArg0]
484 if (direct_code != static_cast<uintptr_t>(-1)) {
Mathieu Chartier921d6eb2015-03-13 16:32:44 -0700485 cg->LoadConstantWide(cg->TargetPtrReg(kInvokeTgt), direct_code);
Vladimir Marko7c2ad5a2014-09-24 12:42:55 +0100486 } else if (Arm64UseRelativeCall(cu, target_method)) {
487 // Defer to linker patch.
488 } else {
489 cg->LoadCodeAddress(target_method, type, kInvokeTgt);
490 }
491 if (direct_method != static_cast<uintptr_t>(-1)) {
Mathieu Chartier921d6eb2015-03-13 16:32:44 -0700492 cg->LoadConstantWide(cg->TargetReg(kArg0, kRef), direct_method);
Vladimir Marko7c2ad5a2014-09-24 12:42:55 +0100493 } else {
494 cg->LoadMethodAddress(target_method, type, kArg0);
495 }
496 break;
497 default:
498 return -1;
499 }
500 } else {
Vladimir Marko20f85592015-03-19 10:07:02 +0000501 bool use_pc_rel = cg->CanUseOpPcRelDexCacheArrayLoad();
Mathieu Chartiere401d142015-04-22 13:56:20 -0700502 RegStorage arg0_ref = cg->TargetPtrReg(kArg0);
Vladimir Marko7c2ad5a2014-09-24 12:42:55 +0100503 switch (state) {
504 case 0: // Get the current Method* [sets kArg0]
505 // TUNING: we can save a reg copy if Method* has been promoted.
Vladimir Marko20f85592015-03-19 10:07:02 +0000506 if (!use_pc_rel) {
507 cg->LoadCurrMethodDirect(arg0_ref);
508 break;
509 }
510 ++state;
511 FALLTHROUGH_INTENDED;
Vladimir Marko7c2ad5a2014-09-24 12:42:55 +0100512 case 1: // Get method->dex_cache_resolved_methods_
Vladimir Marko20f85592015-03-19 10:07:02 +0000513 if (!use_pc_rel) {
Vladimir Marko05792b92015-08-03 11:56:49 +0100514 cg->LoadBaseDisp(arg0_ref,
515 ArtMethod::DexCacheResolvedMethodsOffset(kArm64PointerSize).Int32Value(),
516 arg0_ref,
517 k64,
518 kNotVolatile);
Vladimir Marko20f85592015-03-19 10:07:02 +0000519 }
Vladimir Marko7c2ad5a2014-09-24 12:42:55 +0100520 // Set up direct code if known.
521 if (direct_code != 0) {
522 if (direct_code != static_cast<uintptr_t>(-1)) {
Mathieu Chartier921d6eb2015-03-13 16:32:44 -0700523 cg->LoadConstantWide(cg->TargetPtrReg(kInvokeTgt), direct_code);
Vladimir Marko7c2ad5a2014-09-24 12:42:55 +0100524 } else if (Arm64UseRelativeCall(cu, target_method)) {
525 // Defer to linker patch.
526 } else {
527 CHECK_LT(target_method.dex_method_index, target_method.dex_file->NumMethodIds());
528 cg->LoadCodeAddress(target_method, type, kInvokeTgt);
529 }
530 }
Vladimir Marko20f85592015-03-19 10:07:02 +0000531 if (!use_pc_rel || direct_code != 0) {
532 break;
533 }
534 ++state;
535 FALLTHROUGH_INTENDED;
Vladimir Marko7c2ad5a2014-09-24 12:42:55 +0100536 case 2: // Grab target method*
537 CHECK_EQ(cu->dex_file, target_method.dex_file);
Vladimir Marko20f85592015-03-19 10:07:02 +0000538 if (!use_pc_rel) {
Mathieu Chartiere401d142015-04-22 13:56:20 -0700539 cg->LoadWordDisp(arg0_ref,
Vladimir Marko05792b92015-08-03 11:56:49 +0100540 cg->GetCachePointerOffset(target_method.dex_method_index,
541 kArm64PointerSize),
542 arg0_ref);
Vladimir Marko20f85592015-03-19 10:07:02 +0000543 } else {
544 size_t offset = cg->dex_cache_arrays_layout_.MethodOffset(target_method.dex_method_index);
Mathieu Chartiere401d142015-04-22 13:56:20 -0700545 cg->OpPcRelDexCacheArrayLoad(cu->dex_file, offset, arg0_ref, true);
Vladimir Marko20f85592015-03-19 10:07:02 +0000546 }
Vladimir Marko7c2ad5a2014-09-24 12:42:55 +0100547 break;
548 case 3: // Grab the code from the method*
549 if (direct_code == 0) {
550 // kInvokeTgt := arg0_ref->entrypoint
551 cg->LoadWordDisp(arg0_ref,
Mathieu Chartiere401d142015-04-22 13:56:20 -0700552 ArtMethod::EntryPointFromQuickCompiledCodeOffset(
Mathieu Chartier2d721012014-11-10 11:08:06 -0800553 kArm64PointerSize).Int32Value(), cg->TargetPtrReg(kInvokeTgt));
Vladimir Marko7c2ad5a2014-09-24 12:42:55 +0100554 }
555 break;
556 default:
557 return -1;
558 }
559 }
560 return state + 1;
561}
562
563NextCallInsn Arm64Mir2Lir::GetNextSDCallInsn() {
564 return Arm64NextSDCallInsn;
565}
566
567LIR* Arm64Mir2Lir::CallWithLinkerFixup(const MethodReference& target_method, InvokeType type) {
568 // For ARM64, just generate a relative BL instruction that will be filled in at 'link time'.
569 // If the target turns out to be too far, the linker will generate a thunk for dispatch.
570 int target_method_idx = target_method.dex_method_index;
571 const DexFile* target_dex_file = target_method.dex_file;
572
573 // Generate the call instruction and save index, dex_file, and type.
574 // NOTE: Method deduplication takes linker patches into account, so we can just pass 0
575 // as a placeholder for the offset.
576 LIR* call = RawLIR(current_dalvik_offset_, kA64Bl1t, 0,
Vladimir Markof6737f72015-03-23 17:05:14 +0000577 target_method_idx, WrapPointer(target_dex_file), type);
Vladimir Marko7c2ad5a2014-09-24 12:42:55 +0100578 AppendLIR(call);
579 call_method_insns_.push_back(call);
580 return call;
581}
582
583LIR* Arm64Mir2Lir::GenCallInsn(const MirMethodLoweringInfo& method_info) {
584 LIR* call_insn;
585 if (method_info.FastPath() && Arm64UseRelativeCall(cu_, method_info.GetTargetMethod()) &&
586 (method_info.GetSharpType() == kDirect || method_info.GetSharpType() == kStatic) &&
587 method_info.DirectCode() == static_cast<uintptr_t>(-1)) {
588 call_insn = CallWithLinkerFixup(method_info.GetTargetMethod(), method_info.GetSharpType());
589 } else {
590 call_insn = OpReg(kOpBlx, TargetPtrReg(kInvokeTgt));
591 }
592 return call_insn;
593}
594
Matteo Franchin43ec8732014-03-31 15:00:14 +0100595} // namespace art