blob: 6015a6d4d99d9618794b329f28968d8ce9abde9b [file] [log] [blame]
Mark Mendell09ed1a32015-03-25 08:30:06 -04001/*
2 * Copyright (C) 2015 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 "intrinsics_x86.h"
18
Andreas Gampe21030dd2015-05-07 14:46:15 -070019#include <limits>
20
Mark Mendellfb8d2792015-03-31 22:16:59 -040021#include "arch/x86/instruction_set_features_x86.h"
Mathieu Chartiere401d142015-04-22 13:56:20 -070022#include "art_method.h"
Mark Mendelld5897672015-08-12 21:16:41 -040023#include "base/bit_utils.h"
Mark Mendell09ed1a32015-03-25 08:30:06 -040024#include "code_generator_x86.h"
Andra Danciu1ca6f322020-08-12 08:58:07 +000025#include "data_type-inl.h"
Mark Mendell09ed1a32015-03-25 08:30:06 -040026#include "entrypoints/quick/quick_entrypoints.h"
Andreas Gampe09659c22017-09-18 18:23:32 -070027#include "heap_poisoning.h"
Mark Mendell09ed1a32015-03-25 08:30:06 -040028#include "intrinsics.h"
Andreas Gampe85b62f22015-09-09 13:15:38 -070029#include "intrinsics_utils.h"
Andreas Gampec6ea7d02017-02-01 16:46:28 -080030#include "lock_word.h"
Mark Mendell09ed1a32015-03-25 08:30:06 -040031#include "mirror/array-inl.h"
Andreas Gampec15a2f42017-04-21 12:09:39 -070032#include "mirror/object_array-inl.h"
Andreas Gampec6ea7d02017-02-01 16:46:28 -080033#include "mirror/reference.h"
Mark Mendell09ed1a32015-03-25 08:30:06 -040034#include "mirror/string.h"
Andra Danciue3e187f2020-07-30 12:19:31 +000035#include "mirror/var_handle.h"
Andreas Gampec6ea7d02017-02-01 16:46:28 -080036#include "scoped_thread_state_change-inl.h"
Andreas Gampeb486a982017-06-01 13:45:54 -070037#include "thread-current-inl.h"
Mark Mendell09ed1a32015-03-25 08:30:06 -040038#include "utils/x86/assembler_x86.h"
39#include "utils/x86/constants_x86.h"
40
Vladimir Marko0a516052019-10-14 13:00:44 +000041namespace art {
Mark Mendell09ed1a32015-03-25 08:30:06 -040042
43namespace x86 {
44
Mark Mendellfb8d2792015-03-31 22:16:59 -040045IntrinsicLocationsBuilderX86::IntrinsicLocationsBuilderX86(CodeGeneratorX86* codegen)
Vladimir Markoca6fff82017-10-03 14:49:14 +010046 : allocator_(codegen->GetGraph()->GetAllocator()),
Mark P Mendell2f10a5f2016-01-25 14:47:50 +000047 codegen_(codegen) {
Mark Mendellfb8d2792015-03-31 22:16:59 -040048}
49
50
Mark Mendell09ed1a32015-03-25 08:30:06 -040051X86Assembler* IntrinsicCodeGeneratorX86::GetAssembler() {
Roland Levillainb488b782015-10-22 11:38:49 +010052 return down_cast<X86Assembler*>(codegen_->GetAssembler());
Mark Mendell09ed1a32015-03-25 08:30:06 -040053}
54
55ArenaAllocator* IntrinsicCodeGeneratorX86::GetAllocator() {
Vladimir Markoca6fff82017-10-03 14:49:14 +010056 return codegen_->GetGraph()->GetAllocator();
Mark Mendell09ed1a32015-03-25 08:30:06 -040057}
58
59bool IntrinsicLocationsBuilderX86::TryDispatch(HInvoke* invoke) {
60 Dispatch(invoke);
61 LocationSummary* res = invoke->GetLocations();
Roland Levillain0d5a2812015-11-13 10:07:31 +000062 if (res == nullptr) {
63 return false;
64 }
Roland Levillain0d5a2812015-11-13 10:07:31 +000065 return res->Intrinsified();
Mark Mendell09ed1a32015-03-25 08:30:06 -040066}
67
Andreas Gampe85b62f22015-09-09 13:15:38 -070068using IntrinsicSlowPathX86 = IntrinsicSlowPath<InvokeDexCallingConventionVisitorX86>;
Mark Mendell09ed1a32015-03-25 08:30:06 -040069
Roland Levillain0b671c02016-08-19 12:02:34 +010070// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
71#define __ down_cast<X86Assembler*>(codegen->GetAssembler())-> // NOLINT
72
73// Slow path implementing the SystemArrayCopy intrinsic copy loop with read barriers.
74class ReadBarrierSystemArrayCopySlowPathX86 : public SlowPathCode {
75 public:
76 explicit ReadBarrierSystemArrayCopySlowPathX86(HInstruction* instruction)
77 : SlowPathCode(instruction) {
78 DCHECK(kEmitCompilerReadBarrier);
79 DCHECK(kUseBakerReadBarrier);
80 }
81
Roland Levillainbbc6e7e2018-08-24 16:58:47 +010082 void EmitNativeCode(CodeGenerator* codegen) override {
Roland Levillain0b671c02016-08-19 12:02:34 +010083 CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen);
84 LocationSummary* locations = instruction_->GetLocations();
85 DCHECK(locations->CanCall());
86 DCHECK(instruction_->IsInvokeStaticOrDirect())
87 << "Unexpected instruction in read barrier arraycopy slow path: "
88 << instruction_->DebugName();
89 DCHECK(instruction_->GetLocations()->Intrinsified());
90 DCHECK_EQ(instruction_->AsInvoke()->GetIntrinsic(), Intrinsics::kSystemArrayCopy);
91
Vladimir Marko0ebe0d82017-09-21 22:50:39 +010092 int32_t element_size = DataType::Size(DataType::Type::kReference);
Roland Levillain0b671c02016-08-19 12:02:34 +010093 uint32_t offset = mirror::Array::DataOffset(element_size).Uint32Value();
94
95 Register src = locations->InAt(0).AsRegister<Register>();
96 Location src_pos = locations->InAt(1);
97 Register dest = locations->InAt(2).AsRegister<Register>();
98 Location dest_pos = locations->InAt(3);
99 Location length = locations->InAt(4);
100 Location temp1_loc = locations->GetTemp(0);
101 Register temp1 = temp1_loc.AsRegister<Register>();
102 Register temp2 = locations->GetTemp(1).AsRegister<Register>();
103 Register temp3 = locations->GetTemp(2).AsRegister<Register>();
104
105 __ Bind(GetEntryLabel());
106 // In this code path, registers `temp1`, `temp2`, and `temp3`
107 // (resp.) are not used for the base source address, the base
108 // destination address, and the end source address (resp.), as in
109 // other SystemArrayCopy intrinsic code paths. Instead they are
110 // (resp.) used for:
111 // - the loop index (`i`);
112 // - the source index (`src_index`) and the loaded (source)
113 // reference (`value`); and
114 // - the destination index (`dest_index`).
115
116 // i = 0
117 __ xorl(temp1, temp1);
118 NearLabel loop;
119 __ Bind(&loop);
120 // value = src_array[i + src_pos]
121 if (src_pos.IsConstant()) {
122 int32_t constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
123 int32_t adjusted_offset = offset + constant * element_size;
124 __ movl(temp2, Address(src, temp1, ScaleFactor::TIMES_4, adjusted_offset));
125 } else {
126 __ leal(temp2, Address(src_pos.AsRegister<Register>(), temp1, ScaleFactor::TIMES_1, 0));
127 __ movl(temp2, Address(src, temp2, ScaleFactor::TIMES_4, offset));
128 }
129 __ MaybeUnpoisonHeapReference(temp2);
130 // TODO: Inline the mark bit check before calling the runtime?
131 // value = ReadBarrier::Mark(value)
132 // No need to save live registers; it's taken care of by the
133 // entrypoint. Also, there is no need to update the stack mask,
134 // as this runtime call will not trigger a garbage collection.
135 // (See ReadBarrierMarkSlowPathX86::EmitNativeCode for more
136 // explanations.)
137 DCHECK_NE(temp2, ESP);
138 DCHECK(0 <= temp2 && temp2 < kNumberOfCpuRegisters) << temp2;
Roland Levillain97c46462017-05-11 14:04:03 +0100139 int32_t entry_point_offset = Thread::ReadBarrierMarkEntryPointsOffset<kX86PointerSize>(temp2);
Roland Levillain0b671c02016-08-19 12:02:34 +0100140 // This runtime call does not require a stack map.
141 x86_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
142 __ MaybePoisonHeapReference(temp2);
143 // dest_array[i + dest_pos] = value
144 if (dest_pos.IsConstant()) {
145 int32_t constant = dest_pos.GetConstant()->AsIntConstant()->GetValue();
146 int32_t adjusted_offset = offset + constant * element_size;
147 __ movl(Address(dest, temp1, ScaleFactor::TIMES_4, adjusted_offset), temp2);
148 } else {
149 __ leal(temp3, Address(dest_pos.AsRegister<Register>(), temp1, ScaleFactor::TIMES_1, 0));
150 __ movl(Address(dest, temp3, ScaleFactor::TIMES_4, offset), temp2);
151 }
152 // ++i
153 __ addl(temp1, Immediate(1));
154 // if (i != length) goto loop
155 x86_codegen->GenerateIntCompare(temp1_loc, length);
156 __ j(kNotEqual, &loop);
157 __ jmp(GetExitLabel());
158 }
159
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100160 const char* GetDescription() const override { return "ReadBarrierSystemArrayCopySlowPathX86"; }
Roland Levillain0b671c02016-08-19 12:02:34 +0100161
162 private:
163 DISALLOW_COPY_AND_ASSIGN(ReadBarrierSystemArrayCopySlowPathX86);
164};
165
166#undef __
167
Mark Mendell09ed1a32015-03-25 08:30:06 -0400168#define __ assembler->
169
Vladimir Markoca6fff82017-10-03 14:49:14 +0100170static void CreateFPToIntLocations(ArenaAllocator* allocator, HInvoke* invoke, bool is64bit) {
171 LocationSummary* locations =
172 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Mark Mendell09ed1a32015-03-25 08:30:06 -0400173 locations->SetInAt(0, Location::RequiresFpuRegister());
174 locations->SetOut(Location::RequiresRegister());
175 if (is64bit) {
176 locations->AddTemp(Location::RequiresFpuRegister());
177 }
178}
179
Vladimir Markoca6fff82017-10-03 14:49:14 +0100180static void CreateIntToFPLocations(ArenaAllocator* allocator, HInvoke* invoke, bool is64bit) {
181 LocationSummary* locations =
182 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Mark Mendell09ed1a32015-03-25 08:30:06 -0400183 locations->SetInAt(0, Location::RequiresRegister());
184 locations->SetOut(Location::RequiresFpuRegister());
185 if (is64bit) {
186 locations->AddTemp(Location::RequiresFpuRegister());
187 locations->AddTemp(Location::RequiresFpuRegister());
188 }
189}
190
191static void MoveFPToInt(LocationSummary* locations, bool is64bit, X86Assembler* assembler) {
192 Location input = locations->InAt(0);
193 Location output = locations->Out();
194 if (is64bit) {
195 // Need to use the temporary.
196 XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
197 __ movsd(temp, input.AsFpuRegister<XmmRegister>());
198 __ movd(output.AsRegisterPairLow<Register>(), temp);
199 __ psrlq(temp, Immediate(32));
200 __ movd(output.AsRegisterPairHigh<Register>(), temp);
201 } else {
202 __ movd(output.AsRegister<Register>(), input.AsFpuRegister<XmmRegister>());
203 }
204}
205
206static void MoveIntToFP(LocationSummary* locations, bool is64bit, X86Assembler* assembler) {
207 Location input = locations->InAt(0);
208 Location output = locations->Out();
209 if (is64bit) {
210 // Need to use the temporary.
211 XmmRegister temp1 = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
212 XmmRegister temp2 = locations->GetTemp(1).AsFpuRegister<XmmRegister>();
213 __ movd(temp1, input.AsRegisterPairLow<Register>());
214 __ movd(temp2, input.AsRegisterPairHigh<Register>());
215 __ punpckldq(temp1, temp2);
216 __ movsd(output.AsFpuRegister<XmmRegister>(), temp1);
217 } else {
218 __ movd(output.AsFpuRegister<XmmRegister>(), input.AsRegister<Register>());
219 }
220}
221
222void IntrinsicLocationsBuilderX86::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
Andreas Gampe3db70682018-12-26 15:12:03 -0800223 CreateFPToIntLocations(allocator_, invoke, /* is64bit= */ true);
Mark Mendell09ed1a32015-03-25 08:30:06 -0400224}
225void IntrinsicLocationsBuilderX86::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
Andreas Gampe3db70682018-12-26 15:12:03 -0800226 CreateIntToFPLocations(allocator_, invoke, /* is64bit= */ true);
Mark Mendell09ed1a32015-03-25 08:30:06 -0400227}
228
229void IntrinsicCodeGeneratorX86::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
Andreas Gampe3db70682018-12-26 15:12:03 -0800230 MoveFPToInt(invoke->GetLocations(), /* is64bit= */ true, GetAssembler());
Mark Mendell09ed1a32015-03-25 08:30:06 -0400231}
232void IntrinsicCodeGeneratorX86::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
Andreas Gampe3db70682018-12-26 15:12:03 -0800233 MoveIntToFP(invoke->GetLocations(), /* is64bit= */ true, GetAssembler());
Mark Mendell09ed1a32015-03-25 08:30:06 -0400234}
235
236void IntrinsicLocationsBuilderX86::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
Andreas Gampe3db70682018-12-26 15:12:03 -0800237 CreateFPToIntLocations(allocator_, invoke, /* is64bit= */ false);
Mark Mendell09ed1a32015-03-25 08:30:06 -0400238}
239void IntrinsicLocationsBuilderX86::VisitFloatIntBitsToFloat(HInvoke* invoke) {
Andreas Gampe3db70682018-12-26 15:12:03 -0800240 CreateIntToFPLocations(allocator_, invoke, /* is64bit= */ false);
Mark Mendell09ed1a32015-03-25 08:30:06 -0400241}
242
243void IntrinsicCodeGeneratorX86::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
Andreas Gampe3db70682018-12-26 15:12:03 -0800244 MoveFPToInt(invoke->GetLocations(), /* is64bit= */ false, GetAssembler());
Mark Mendell09ed1a32015-03-25 08:30:06 -0400245}
246void IntrinsicCodeGeneratorX86::VisitFloatIntBitsToFloat(HInvoke* invoke) {
Andreas Gampe3db70682018-12-26 15:12:03 -0800247 MoveIntToFP(invoke->GetLocations(), /* is64bit= */ false, GetAssembler());
Mark Mendell09ed1a32015-03-25 08:30:06 -0400248}
249
Vladimir Markoca6fff82017-10-03 14:49:14 +0100250static void CreateIntToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) {
251 LocationSummary* locations =
252 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Mark Mendell09ed1a32015-03-25 08:30:06 -0400253 locations->SetInAt(0, Location::RequiresRegister());
254 locations->SetOut(Location::SameAsFirstInput());
255}
256
Vladimir Markoca6fff82017-10-03 14:49:14 +0100257static void CreateLongToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) {
258 LocationSummary* locations =
259 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Mark Mendell09ed1a32015-03-25 08:30:06 -0400260 locations->SetInAt(0, Location::RequiresRegister());
261 locations->SetOut(Location::RequiresRegister());
262}
263
Vladimir Markoca6fff82017-10-03 14:49:14 +0100264static void CreateLongToLongLocations(ArenaAllocator* allocator, HInvoke* invoke) {
265 LocationSummary* locations =
266 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Mark Mendell09ed1a32015-03-25 08:30:06 -0400267 locations->SetInAt(0, Location::RequiresRegister());
268 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
269}
270
271static void GenReverseBytes(LocationSummary* locations,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100272 DataType::Type size,
Mark Mendell09ed1a32015-03-25 08:30:06 -0400273 X86Assembler* assembler) {
274 Register out = locations->Out().AsRegister<Register>();
275
276 switch (size) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100277 case DataType::Type::kInt16:
Mark Mendell09ed1a32015-03-25 08:30:06 -0400278 // TODO: Can be done with an xchg of 8b registers. This is straight from Quick.
279 __ bswapl(out);
280 __ sarl(out, Immediate(16));
281 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100282 case DataType::Type::kInt32:
Mark Mendell09ed1a32015-03-25 08:30:06 -0400283 __ bswapl(out);
284 break;
285 default:
286 LOG(FATAL) << "Unexpected size for reverse-bytes: " << size;
287 UNREACHABLE();
288 }
289}
290
291void IntrinsicLocationsBuilderX86::VisitIntegerReverseBytes(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100292 CreateIntToIntLocations(allocator_, invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -0400293}
294
295void IntrinsicCodeGeneratorX86::VisitIntegerReverseBytes(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100296 GenReverseBytes(invoke->GetLocations(), DataType::Type::kInt32, GetAssembler());
Mark Mendell09ed1a32015-03-25 08:30:06 -0400297}
298
Mark Mendell58d25fd2015-04-03 14:52:31 -0400299void IntrinsicLocationsBuilderX86::VisitLongReverseBytes(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100300 CreateLongToLongLocations(allocator_, invoke);
Mark Mendell58d25fd2015-04-03 14:52:31 -0400301}
302
303void IntrinsicCodeGeneratorX86::VisitLongReverseBytes(HInvoke* invoke) {
304 LocationSummary* locations = invoke->GetLocations();
305 Location input = locations->InAt(0);
306 Register input_lo = input.AsRegisterPairLow<Register>();
307 Register input_hi = input.AsRegisterPairHigh<Register>();
308 Location output = locations->Out();
309 Register output_lo = output.AsRegisterPairLow<Register>();
310 Register output_hi = output.AsRegisterPairHigh<Register>();
311
312 X86Assembler* assembler = GetAssembler();
313 // Assign the inputs to the outputs, mixing low/high.
314 __ movl(output_lo, input_hi);
315 __ movl(output_hi, input_lo);
316 __ bswapl(output_lo);
317 __ bswapl(output_hi);
318}
319
Mark Mendell09ed1a32015-03-25 08:30:06 -0400320void IntrinsicLocationsBuilderX86::VisitShortReverseBytes(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100321 CreateIntToIntLocations(allocator_, invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -0400322}
323
324void IntrinsicCodeGeneratorX86::VisitShortReverseBytes(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100325 GenReverseBytes(invoke->GetLocations(), DataType::Type::kInt16, GetAssembler());
Mark Mendell09ed1a32015-03-25 08:30:06 -0400326}
327
Vladimir Markoca6fff82017-10-03 14:49:14 +0100328static void CreateFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) {
329 LocationSummary* locations =
330 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Mark Mendell09ed1a32015-03-25 08:30:06 -0400331 locations->SetInAt(0, Location::RequiresFpuRegister());
332 locations->SetOut(Location::RequiresFpuRegister());
333}
334
335void IntrinsicLocationsBuilderX86::VisitMathSqrt(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100336 CreateFPToFPLocations(allocator_, invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -0400337}
338
339void IntrinsicCodeGeneratorX86::VisitMathSqrt(HInvoke* invoke) {
340 LocationSummary* locations = invoke->GetLocations();
341 XmmRegister in = locations->InAt(0).AsFpuRegister<XmmRegister>();
342 XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
343
344 GetAssembler()->sqrtsd(out, in);
345}
346
Vladimir Markoca6fff82017-10-03 14:49:14 +0100347static void CreateSSE41FPToFPLocations(ArenaAllocator* allocator,
348 HInvoke* invoke,
349 CodeGeneratorX86* codegen) {
Mark Mendellfb8d2792015-03-31 22:16:59 -0400350 // Do we have instruction support?
Vladimir Marko66704db2020-06-08 14:04:27 +0100351 if (!codegen->GetInstructionSetFeatures().HasSSE4_1()) {
Mark Mendellfb8d2792015-03-31 22:16:59 -0400352 return;
353 }
354
Vladimir Marko66704db2020-06-08 14:04:27 +0100355 CreateFPToFPLocations(allocator, invoke);
Mark Mendellfb8d2792015-03-31 22:16:59 -0400356}
357
Vladimir Marko66704db2020-06-08 14:04:27 +0100358static void GenSSE41FPToFPIntrinsic(HInvoke* invoke, X86Assembler* assembler, int round_mode) {
Mark Mendellfb8d2792015-03-31 22:16:59 -0400359 LocationSummary* locations = invoke->GetLocations();
Vladimir Marko66704db2020-06-08 14:04:27 +0100360 DCHECK(!locations->WillCall());
361 XmmRegister in = locations->InAt(0).AsFpuRegister<XmmRegister>();
362 XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
363 __ roundsd(out, in, Immediate(round_mode));
Mark Mendellfb8d2792015-03-31 22:16:59 -0400364}
365
366void IntrinsicLocationsBuilderX86::VisitMathCeil(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100367 CreateSSE41FPToFPLocations(allocator_, invoke, codegen_);
Mark Mendellfb8d2792015-03-31 22:16:59 -0400368}
369
370void IntrinsicCodeGeneratorX86::VisitMathCeil(HInvoke* invoke) {
Vladimir Marko66704db2020-06-08 14:04:27 +0100371 GenSSE41FPToFPIntrinsic(invoke, GetAssembler(), 2);
Mark Mendellfb8d2792015-03-31 22:16:59 -0400372}
373
374void IntrinsicLocationsBuilderX86::VisitMathFloor(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100375 CreateSSE41FPToFPLocations(allocator_, invoke, codegen_);
Mark Mendellfb8d2792015-03-31 22:16:59 -0400376}
377
378void IntrinsicCodeGeneratorX86::VisitMathFloor(HInvoke* invoke) {
Vladimir Marko66704db2020-06-08 14:04:27 +0100379 GenSSE41FPToFPIntrinsic(invoke, GetAssembler(), 1);
Mark Mendellfb8d2792015-03-31 22:16:59 -0400380}
381
382void IntrinsicLocationsBuilderX86::VisitMathRint(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100383 CreateSSE41FPToFPLocations(allocator_, invoke, codegen_);
Mark Mendellfb8d2792015-03-31 22:16:59 -0400384}
385
386void IntrinsicCodeGeneratorX86::VisitMathRint(HInvoke* invoke) {
Vladimir Marko66704db2020-06-08 14:04:27 +0100387 GenSSE41FPToFPIntrinsic(invoke, GetAssembler(), 0);
Mark Mendellfb8d2792015-03-31 22:16:59 -0400388}
389
Mark Mendellfb8d2792015-03-31 22:16:59 -0400390void IntrinsicLocationsBuilderX86::VisitMathRoundFloat(HInvoke* invoke) {
391 // Do we have instruction support?
Vladimir Marko66704db2020-06-08 14:04:27 +0100392 if (!codegen_->GetInstructionSetFeatures().HasSSE4_1()) {
Mark Mendellfb8d2792015-03-31 22:16:59 -0400393 return;
394 }
395
Vladimir Marko66704db2020-06-08 14:04:27 +0100396 HInvokeStaticOrDirect* static_or_direct = invoke->AsInvokeStaticOrDirect();
397 DCHECK(static_or_direct != nullptr);
Vladimir Markoca6fff82017-10-03 14:49:14 +0100398 LocationSummary* locations =
Vladimir Marko66704db2020-06-08 14:04:27 +0100399 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
400 locations->SetInAt(0, Location::RequiresFpuRegister());
401 if (static_or_direct->HasSpecialInput() &&
402 invoke->InputAt(
403 static_or_direct->GetSpecialInputIndex())->IsX86ComputeBaseMethodAddress()) {
404 locations->SetInAt(1, Location::RequiresRegister());
405 }
406 locations->SetOut(Location::RequiresRegister());
407 locations->AddTemp(Location::RequiresFpuRegister());
408 locations->AddTemp(Location::RequiresFpuRegister());
Mark Mendellfb8d2792015-03-31 22:16:59 -0400409}
410
411void IntrinsicCodeGeneratorX86::VisitMathRoundFloat(HInvoke* invoke) {
412 LocationSummary* locations = invoke->GetLocations();
Vladimir Marko66704db2020-06-08 14:04:27 +0100413 DCHECK(!locations->WillCall());
Mark Mendellfb8d2792015-03-31 22:16:59 -0400414
Mark Mendellfb8d2792015-03-31 22:16:59 -0400415 XmmRegister in = locations->InAt(0).AsFpuRegister<XmmRegister>();
Aart Bik2c9f4952016-08-01 16:52:27 -0700416 XmmRegister t1 = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
417 XmmRegister t2 = locations->GetTemp(1).AsFpuRegister<XmmRegister>();
Mark Mendellfb8d2792015-03-31 22:16:59 -0400418 Register out = locations->Out().AsRegister<Register>();
Aart Bik2c9f4952016-08-01 16:52:27 -0700419 NearLabel skip_incr, done;
Mark Mendellfb8d2792015-03-31 22:16:59 -0400420 X86Assembler* assembler = GetAssembler();
421
Aart Bik2c9f4952016-08-01 16:52:27 -0700422 // Since no direct x86 rounding instruction matches the required semantics,
423 // this intrinsic is implemented as follows:
424 // result = floor(in);
425 // if (in - result >= 0.5f)
426 // result = result + 1.0f;
427 __ movss(t2, in);
428 __ roundss(t1, in, Immediate(1));
429 __ subss(t2, t1);
Aart Bik0cf8d9c2016-08-10 14:05:54 -0700430 if (locations->GetInputCount() == 2 && locations->InAt(1).IsValid()) {
431 // Direct constant area available.
Nicolas Geoffray133719e2017-01-22 15:44:39 +0000432 HX86ComputeBaseMethodAddress* method_address =
433 invoke->InputAt(1)->AsX86ComputeBaseMethodAddress();
Aart Bik0cf8d9c2016-08-10 14:05:54 -0700434 Register constant_area = locations->InAt(1).AsRegister<Register>();
Nicolas Geoffray133719e2017-01-22 15:44:39 +0000435 __ comiss(t2, codegen_->LiteralInt32Address(bit_cast<int32_t, float>(0.5f),
436 method_address,
437 constant_area));
Aart Bik0cf8d9c2016-08-10 14:05:54 -0700438 __ j(kBelow, &skip_incr);
Nicolas Geoffray133719e2017-01-22 15:44:39 +0000439 __ addss(t1, codegen_->LiteralInt32Address(bit_cast<int32_t, float>(1.0f),
440 method_address,
441 constant_area));
Aart Bik0cf8d9c2016-08-10 14:05:54 -0700442 __ Bind(&skip_incr);
443 } else {
444 // No constant area: go through stack.
445 __ pushl(Immediate(bit_cast<int32_t, float>(0.5f)));
446 __ pushl(Immediate(bit_cast<int32_t, float>(1.0f)));
447 __ comiss(t2, Address(ESP, 4));
448 __ j(kBelow, &skip_incr);
449 __ addss(t1, Address(ESP, 0));
450 __ Bind(&skip_incr);
451 __ addl(ESP, Immediate(8));
452 }
Mark Mendellfb8d2792015-03-31 22:16:59 -0400453
Aart Bik2c9f4952016-08-01 16:52:27 -0700454 // Final conversion to an integer. Unfortunately this also does not have a
455 // direct x86 instruction, since NaN should map to 0 and large positive
456 // values need to be clipped to the extreme value.
Mark Mendellfb8d2792015-03-31 22:16:59 -0400457 __ movl(out, Immediate(kPrimIntMax));
Aart Bik2c9f4952016-08-01 16:52:27 -0700458 __ cvtsi2ss(t2, out);
459 __ comiss(t1, t2);
460 __ j(kAboveEqual, &done); // clipped to max (already in out), does not jump on unordered
461 __ movl(out, Immediate(0)); // does not change flags
462 __ j(kUnordered, &done); // NaN mapped to 0 (just moved in out)
463 __ cvttss2si(out, t1);
Mark Mendellfb8d2792015-03-31 22:16:59 -0400464 __ Bind(&done);
465}
466
Vladimir Markoca6fff82017-10-03 14:49:14 +0100467static void CreateFPToFPCallLocations(ArenaAllocator* allocator, HInvoke* invoke) {
468 LocationSummary* locations =
469 new (allocator) LocationSummary(invoke, LocationSummary::kCallOnMainOnly, kIntrinsified);
Mark Mendella4f12202015-08-06 15:23:34 -0400470 InvokeRuntimeCallingConvention calling_convention;
471 locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
472 locations->SetOut(Location::FpuRegisterLocation(XMM0));
473}
474
475static void GenFPToFPCall(HInvoke* invoke, CodeGeneratorX86* codegen, QuickEntrypointEnum entry) {
476 LocationSummary* locations = invoke->GetLocations();
477 DCHECK(locations->WillCall());
478 DCHECK(invoke->IsInvokeStaticOrDirect());
479 X86Assembler* assembler = codegen->GetAssembler();
480
481 // We need some place to pass the parameters.
482 __ subl(ESP, Immediate(16));
483 __ cfi().AdjustCFAOffset(16);
484
485 // Pass the parameters at the bottom of the stack.
486 __ movsd(Address(ESP, 0), XMM0);
487
488 // If we have a second parameter, pass it next.
489 if (invoke->GetNumberOfArguments() == 2) {
490 __ movsd(Address(ESP, 8), XMM1);
491 }
492
493 // Now do the actual call.
Serban Constantinescuba45db02016-07-12 22:53:02 +0100494 codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
Mark Mendella4f12202015-08-06 15:23:34 -0400495
496 // Extract the return value from the FP stack.
497 __ fstpl(Address(ESP, 0));
498 __ movsd(XMM0, Address(ESP, 0));
499
500 // And clean up the stack.
501 __ addl(ESP, Immediate(16));
502 __ cfi().AdjustCFAOffset(-16);
Mark Mendella4f12202015-08-06 15:23:34 -0400503}
504
Shalini Salomi Bodapati8943fa12018-11-21 15:36:00 +0530505static void CreateLowestOneBitLocations(ArenaAllocator* allocator, bool is_long, HInvoke* invoke) {
506 LocationSummary* locations =
507 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
508 if (is_long) {
509 locations->SetInAt(0, Location::RequiresRegister());
510 } else {
511 locations->SetInAt(0, Location::Any());
512 }
513 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
514}
515
516static void GenLowestOneBit(X86Assembler* assembler,
517 CodeGeneratorX86* codegen,
518 bool is_long,
519 HInvoke* invoke) {
520 LocationSummary* locations = invoke->GetLocations();
521 Location src = locations->InAt(0);
522 Location out_loc = locations->Out();
523
524 if (invoke->InputAt(0)->IsConstant()) {
525 // Evaluate this at compile time.
526 int64_t value = Int64FromConstant(invoke->InputAt(0)->AsConstant());
527 if (value == 0) {
528 if (is_long) {
529 __ xorl(out_loc.AsRegisterPairLow<Register>(), out_loc.AsRegisterPairLow<Register>());
530 __ xorl(out_loc.AsRegisterPairHigh<Register>(), out_loc.AsRegisterPairHigh<Register>());
531 } else {
532 __ xorl(out_loc.AsRegister<Register>(), out_loc.AsRegister<Register>());
533 }
534 return;
535 }
536 // Nonzero value.
537 value = is_long ? CTZ(static_cast<uint64_t>(value))
538 : CTZ(static_cast<uint32_t>(value));
539 if (is_long) {
540 if (value >= 32) {
541 int shift = value-32;
542 codegen->Load32BitValue(out_loc.AsRegisterPairLow<Register>(), 0);
543 codegen->Load32BitValue(out_loc.AsRegisterPairHigh<Register>(), 1 << shift);
544 } else {
545 codegen->Load32BitValue(out_loc.AsRegisterPairLow<Register>(), 1 << value);
546 codegen->Load32BitValue(out_loc.AsRegisterPairHigh<Register>(), 0);
547 }
548 } else {
549 codegen->Load32BitValue(out_loc.AsRegister<Register>(), 1 << value);
550 }
551 return;
552 }
553 // Handle non constant case
554 if (is_long) {
555 DCHECK(src.IsRegisterPair());
556 Register src_lo = src.AsRegisterPairLow<Register>();
557 Register src_hi = src.AsRegisterPairHigh<Register>();
558
559 Register out_lo = out_loc.AsRegisterPairLow<Register>();
560 Register out_hi = out_loc.AsRegisterPairHigh<Register>();
561
562 __ movl(out_lo, src_lo);
563 __ movl(out_hi, src_hi);
564
565 __ negl(out_lo);
566 __ adcl(out_hi, Immediate(0));
567 __ negl(out_hi);
568
569 __ andl(out_lo, src_lo);
570 __ andl(out_hi, src_hi);
571 } else {
572 if (codegen->GetInstructionSetFeatures().HasAVX2() && src.IsRegister()) {
573 Register out = out_loc.AsRegister<Register>();
574 __ blsi(out, src.AsRegister<Register>());
575 } else {
576 Register out = out_loc.AsRegister<Register>();
577 // Do tmp & -tmp
578 if (src.IsRegister()) {
579 __ movl(out, src.AsRegister<Register>());
580 } else {
581 DCHECK(src.IsStackSlot());
582 __ movl(out, Address(ESP, src.GetStackIndex()));
583 }
584 __ negl(out);
585
586 if (src.IsRegister()) {
587 __ andl(out, src.AsRegister<Register>());
588 } else {
589 __ andl(out, Address(ESP, src.GetStackIndex()));
590 }
591 }
592 }
593}
594
Mark Mendella4f12202015-08-06 15:23:34 -0400595void IntrinsicLocationsBuilderX86::VisitMathCos(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100596 CreateFPToFPCallLocations(allocator_, invoke);
Mark Mendella4f12202015-08-06 15:23:34 -0400597}
598
599void IntrinsicCodeGeneratorX86::VisitMathCos(HInvoke* invoke) {
600 GenFPToFPCall(invoke, codegen_, kQuickCos);
601}
602
603void IntrinsicLocationsBuilderX86::VisitMathSin(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100604 CreateFPToFPCallLocations(allocator_, invoke);
Mark Mendella4f12202015-08-06 15:23:34 -0400605}
606
607void IntrinsicCodeGeneratorX86::VisitMathSin(HInvoke* invoke) {
608 GenFPToFPCall(invoke, codegen_, kQuickSin);
609}
610
611void IntrinsicLocationsBuilderX86::VisitMathAcos(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100612 CreateFPToFPCallLocations(allocator_, invoke);
Mark Mendella4f12202015-08-06 15:23:34 -0400613}
614
615void IntrinsicCodeGeneratorX86::VisitMathAcos(HInvoke* invoke) {
616 GenFPToFPCall(invoke, codegen_, kQuickAcos);
617}
618
619void IntrinsicLocationsBuilderX86::VisitMathAsin(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100620 CreateFPToFPCallLocations(allocator_, invoke);
Mark Mendella4f12202015-08-06 15:23:34 -0400621}
622
623void IntrinsicCodeGeneratorX86::VisitMathAsin(HInvoke* invoke) {
624 GenFPToFPCall(invoke, codegen_, kQuickAsin);
625}
626
627void IntrinsicLocationsBuilderX86::VisitMathAtan(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100628 CreateFPToFPCallLocations(allocator_, invoke);
Mark Mendella4f12202015-08-06 15:23:34 -0400629}
630
631void IntrinsicCodeGeneratorX86::VisitMathAtan(HInvoke* invoke) {
632 GenFPToFPCall(invoke, codegen_, kQuickAtan);
633}
634
635void IntrinsicLocationsBuilderX86::VisitMathCbrt(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100636 CreateFPToFPCallLocations(allocator_, invoke);
Mark Mendella4f12202015-08-06 15:23:34 -0400637}
638
639void IntrinsicCodeGeneratorX86::VisitMathCbrt(HInvoke* invoke) {
640 GenFPToFPCall(invoke, codegen_, kQuickCbrt);
641}
642
643void IntrinsicLocationsBuilderX86::VisitMathCosh(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100644 CreateFPToFPCallLocations(allocator_, invoke);
Mark Mendella4f12202015-08-06 15:23:34 -0400645}
646
647void IntrinsicCodeGeneratorX86::VisitMathCosh(HInvoke* invoke) {
648 GenFPToFPCall(invoke, codegen_, kQuickCosh);
649}
650
651void IntrinsicLocationsBuilderX86::VisitMathExp(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100652 CreateFPToFPCallLocations(allocator_, invoke);
Mark Mendella4f12202015-08-06 15:23:34 -0400653}
654
655void IntrinsicCodeGeneratorX86::VisitMathExp(HInvoke* invoke) {
656 GenFPToFPCall(invoke, codegen_, kQuickExp);
657}
658
659void IntrinsicLocationsBuilderX86::VisitMathExpm1(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100660 CreateFPToFPCallLocations(allocator_, invoke);
Mark Mendella4f12202015-08-06 15:23:34 -0400661}
662
663void IntrinsicCodeGeneratorX86::VisitMathExpm1(HInvoke* invoke) {
664 GenFPToFPCall(invoke, codegen_, kQuickExpm1);
665}
666
667void IntrinsicLocationsBuilderX86::VisitMathLog(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100668 CreateFPToFPCallLocations(allocator_, invoke);
Mark Mendella4f12202015-08-06 15:23:34 -0400669}
670
671void IntrinsicCodeGeneratorX86::VisitMathLog(HInvoke* invoke) {
672 GenFPToFPCall(invoke, codegen_, kQuickLog);
673}
674
675void IntrinsicLocationsBuilderX86::VisitMathLog10(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100676 CreateFPToFPCallLocations(allocator_, invoke);
Mark Mendella4f12202015-08-06 15:23:34 -0400677}
678
679void IntrinsicCodeGeneratorX86::VisitMathLog10(HInvoke* invoke) {
680 GenFPToFPCall(invoke, codegen_, kQuickLog10);
681}
682
683void IntrinsicLocationsBuilderX86::VisitMathSinh(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100684 CreateFPToFPCallLocations(allocator_, invoke);
Mark Mendella4f12202015-08-06 15:23:34 -0400685}
686
687void IntrinsicCodeGeneratorX86::VisitMathSinh(HInvoke* invoke) {
688 GenFPToFPCall(invoke, codegen_, kQuickSinh);
689}
690
691void IntrinsicLocationsBuilderX86::VisitMathTan(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100692 CreateFPToFPCallLocations(allocator_, invoke);
Mark Mendella4f12202015-08-06 15:23:34 -0400693}
694
695void IntrinsicCodeGeneratorX86::VisitMathTan(HInvoke* invoke) {
696 GenFPToFPCall(invoke, codegen_, kQuickTan);
697}
698
699void IntrinsicLocationsBuilderX86::VisitMathTanh(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100700 CreateFPToFPCallLocations(allocator_, invoke);
Mark Mendella4f12202015-08-06 15:23:34 -0400701}
702
703void IntrinsicCodeGeneratorX86::VisitMathTanh(HInvoke* invoke) {
704 GenFPToFPCall(invoke, codegen_, kQuickTanh);
705}
706
Shalini Salomi Bodapati8943fa12018-11-21 15:36:00 +0530707void IntrinsicLocationsBuilderX86::VisitIntegerLowestOneBit(HInvoke* invoke) {
708 CreateLowestOneBitLocations(allocator_, /*is_long=*/ false, invoke);
709}
710void IntrinsicCodeGeneratorX86::VisitIntegerLowestOneBit(HInvoke* invoke) {
711 GenLowestOneBit(GetAssembler(), codegen_, /*is_long=*/ false, invoke);
712}
713
714void IntrinsicLocationsBuilderX86::VisitLongLowestOneBit(HInvoke* invoke) {
715 CreateLowestOneBitLocations(allocator_, /*is_long=*/ true, invoke);
716}
717
718void IntrinsicCodeGeneratorX86::VisitLongLowestOneBit(HInvoke* invoke) {
719 GenLowestOneBit(GetAssembler(), codegen_, /*is_long=*/ true, invoke);
720}
721
Vladimir Markoca6fff82017-10-03 14:49:14 +0100722static void CreateFPFPToFPCallLocations(ArenaAllocator* allocator, HInvoke* invoke) {
723 LocationSummary* locations =
724 new (allocator) LocationSummary(invoke, LocationSummary::kCallOnMainOnly, kIntrinsified);
Mark Mendella4f12202015-08-06 15:23:34 -0400725 InvokeRuntimeCallingConvention calling_convention;
726 locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
727 locations->SetInAt(1, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(1)));
728 locations->SetOut(Location::FpuRegisterLocation(XMM0));
729}
730
Shalini Salomi Bodapati6545ee32021-11-02 20:01:06 +0530731static void CreateFPFPFPToFPCallLocations(ArenaAllocator* allocator, HInvoke* invoke) {
732 DCHECK_EQ(invoke->GetNumberOfArguments(), 3U);
733 LocationSummary* locations =
734 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
735 InvokeRuntimeCallingConvention calling_convention;
736 locations->SetInAt(0, Location::RequiresFpuRegister());
737 locations->SetInAt(1, Location::RequiresFpuRegister());
738 locations->SetInAt(2, Location::RequiresFpuRegister());
739 locations->SetOut(Location::SameAsFirstInput());
740}
741
Mark Mendella4f12202015-08-06 15:23:34 -0400742void IntrinsicLocationsBuilderX86::VisitMathAtan2(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100743 CreateFPFPToFPCallLocations(allocator_, invoke);
Mark Mendella4f12202015-08-06 15:23:34 -0400744}
745
746void IntrinsicCodeGeneratorX86::VisitMathAtan2(HInvoke* invoke) {
747 GenFPToFPCall(invoke, codegen_, kQuickAtan2);
748}
749
Vladimir Marko4d179872018-01-19 14:50:10 +0000750void IntrinsicLocationsBuilderX86::VisitMathPow(HInvoke* invoke) {
751 CreateFPFPToFPCallLocations(allocator_, invoke);
752}
753
754void IntrinsicCodeGeneratorX86::VisitMathPow(HInvoke* invoke) {
755 GenFPToFPCall(invoke, codegen_, kQuickPow);
756}
757
Mark Mendella4f12202015-08-06 15:23:34 -0400758void IntrinsicLocationsBuilderX86::VisitMathHypot(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100759 CreateFPFPToFPCallLocations(allocator_, invoke);
Mark Mendella4f12202015-08-06 15:23:34 -0400760}
761
762void IntrinsicCodeGeneratorX86::VisitMathHypot(HInvoke* invoke) {
763 GenFPToFPCall(invoke, codegen_, kQuickHypot);
764}
765
766void IntrinsicLocationsBuilderX86::VisitMathNextAfter(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100767 CreateFPFPToFPCallLocations(allocator_, invoke);
Mark Mendella4f12202015-08-06 15:23:34 -0400768}
769
770void IntrinsicCodeGeneratorX86::VisitMathNextAfter(HInvoke* invoke) {
771 GenFPToFPCall(invoke, codegen_, kQuickNextAfter);
772}
773
Mark Mendell6bc53a92015-07-01 14:26:52 -0400774void IntrinsicLocationsBuilderX86::VisitSystemArrayCopyChar(HInvoke* invoke) {
775 // We need at least two of the positions or length to be an integer constant,
776 // or else we won't have enough free registers.
777 HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
778 HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
779 HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
780
781 int num_constants =
782 ((src_pos != nullptr) ? 1 : 0)
783 + ((dest_pos != nullptr) ? 1 : 0)
784 + ((length != nullptr) ? 1 : 0);
785
786 if (num_constants < 2) {
787 // Not enough free registers.
788 return;
789 }
790
791 // As long as we are checking, we might as well check to see if the src and dest
792 // positions are >= 0.
793 if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
794 (dest_pos != nullptr && dest_pos->GetValue() < 0)) {
795 // We will have to fail anyways.
796 return;
797 }
798
799 // And since we are already checking, check the length too.
800 if (length != nullptr) {
801 int32_t len = length->GetValue();
802 if (len < 0) {
803 // Just call as normal.
804 return;
805 }
806 }
807
808 // Okay, it is safe to generate inline code.
809 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +0100810 new (allocator_) LocationSummary(invoke, LocationSummary::kCallOnSlowPath, kIntrinsified);
Mark Mendell6bc53a92015-07-01 14:26:52 -0400811 // arraycopy(Object src, int srcPos, Object dest, int destPos, int length).
812 locations->SetInAt(0, Location::RequiresRegister());
813 locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
814 locations->SetInAt(2, Location::RequiresRegister());
815 locations->SetInAt(3, Location::RegisterOrConstant(invoke->InputAt(3)));
816 locations->SetInAt(4, Location::RegisterOrConstant(invoke->InputAt(4)));
817
818 // And we need some temporaries. We will use REP MOVSW, so we need fixed registers.
819 locations->AddTemp(Location::RegisterLocation(ESI));
820 locations->AddTemp(Location::RegisterLocation(EDI));
821 locations->AddTemp(Location::RegisterLocation(ECX));
822}
823
824static void CheckPosition(X86Assembler* assembler,
825 Location pos,
826 Register input,
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +0100827 Location length,
Andreas Gampe85b62f22015-09-09 13:15:38 -0700828 SlowPathCode* slow_path,
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +0100829 Register temp,
830 bool length_is_input_length = false) {
831 // Where is the length in the Array?
Mark Mendell6bc53a92015-07-01 14:26:52 -0400832 const uint32_t length_offset = mirror::Array::LengthOffset().Uint32Value();
833
834 if (pos.IsConstant()) {
835 int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue();
836 if (pos_const == 0) {
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +0100837 if (!length_is_input_length) {
838 // Check that length(input) >= length.
839 if (length.IsConstant()) {
840 __ cmpl(Address(input, length_offset),
841 Immediate(length.GetConstant()->AsIntConstant()->GetValue()));
842 } else {
843 __ cmpl(Address(input, length_offset), length.AsRegister<Register>());
844 }
845 __ j(kLess, slow_path->GetEntryLabel());
846 }
Mark Mendell6bc53a92015-07-01 14:26:52 -0400847 } else {
848 // Check that length(input) >= pos.
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +0100849 __ movl(temp, Address(input, length_offset));
850 __ subl(temp, Immediate(pos_const));
Mark Mendell6bc53a92015-07-01 14:26:52 -0400851 __ j(kLess, slow_path->GetEntryLabel());
852
853 // Check that (length(input) - pos) >= length.
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +0100854 if (length.IsConstant()) {
855 __ cmpl(temp, Immediate(length.GetConstant()->AsIntConstant()->GetValue()));
856 } else {
857 __ cmpl(temp, length.AsRegister<Register>());
858 }
Mark Mendell6bc53a92015-07-01 14:26:52 -0400859 __ j(kLess, slow_path->GetEntryLabel());
860 }
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +0100861 } else if (length_is_input_length) {
862 // The only way the copy can succeed is if pos is zero.
863 Register pos_reg = pos.AsRegister<Register>();
864 __ testl(pos_reg, pos_reg);
865 __ j(kNotEqual, slow_path->GetEntryLabel());
Mark Mendell6bc53a92015-07-01 14:26:52 -0400866 } else {
867 // Check that pos >= 0.
868 Register pos_reg = pos.AsRegister<Register>();
869 __ testl(pos_reg, pos_reg);
870 __ j(kLess, slow_path->GetEntryLabel());
871
872 // Check that pos <= length(input).
873 __ cmpl(Address(input, length_offset), pos_reg);
874 __ j(kLess, slow_path->GetEntryLabel());
875
876 // Check that (length(input) - pos) >= length.
877 __ movl(temp, Address(input, length_offset));
878 __ subl(temp, pos_reg);
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +0100879 if (length.IsConstant()) {
880 __ cmpl(temp, Immediate(length.GetConstant()->AsIntConstant()->GetValue()));
881 } else {
882 __ cmpl(temp, length.AsRegister<Register>());
883 }
Mark Mendell6bc53a92015-07-01 14:26:52 -0400884 __ j(kLess, slow_path->GetEntryLabel());
885 }
886}
887
888void IntrinsicCodeGeneratorX86::VisitSystemArrayCopyChar(HInvoke* invoke) {
889 X86Assembler* assembler = GetAssembler();
890 LocationSummary* locations = invoke->GetLocations();
891
892 Register src = locations->InAt(0).AsRegister<Register>();
893 Location srcPos = locations->InAt(1);
894 Register dest = locations->InAt(2).AsRegister<Register>();
895 Location destPos = locations->InAt(3);
896 Location length = locations->InAt(4);
897
898 // Temporaries that we need for MOVSW.
899 Register src_base = locations->GetTemp(0).AsRegister<Register>();
900 DCHECK_EQ(src_base, ESI);
901 Register dest_base = locations->GetTemp(1).AsRegister<Register>();
902 DCHECK_EQ(dest_base, EDI);
903 Register count = locations->GetTemp(2).AsRegister<Register>();
904 DCHECK_EQ(count, ECX);
905
Vladimir Marko174b2e22017-10-12 13:34:49 +0100906 SlowPathCode* slow_path = new (codegen_->GetScopedAllocator()) IntrinsicSlowPathX86(invoke);
Mark Mendell6bc53a92015-07-01 14:26:52 -0400907 codegen_->AddSlowPath(slow_path);
908
909 // Bail out if the source and destination are the same (to handle overlap).
910 __ cmpl(src, dest);
911 __ j(kEqual, slow_path->GetEntryLabel());
912
913 // Bail out if the source is null.
914 __ testl(src, src);
915 __ j(kEqual, slow_path->GetEntryLabel());
916
917 // Bail out if the destination is null.
918 __ testl(dest, dest);
919 __ j(kEqual, slow_path->GetEntryLabel());
920
921 // If the length is negative, bail out.
922 // We have already checked in the LocationsBuilder for the constant case.
923 if (!length.IsConstant()) {
924 __ cmpl(length.AsRegister<Register>(), length.AsRegister<Register>());
925 __ j(kLess, slow_path->GetEntryLabel());
926 }
927
928 // We need the count in ECX.
929 if (length.IsConstant()) {
930 __ movl(count, Immediate(length.GetConstant()->AsIntConstant()->GetValue()));
931 } else {
932 __ movl(count, length.AsRegister<Register>());
933 }
934
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +0100935 // Validity checks: source. Use src_base as a temporary register.
936 CheckPosition(assembler, srcPos, src, Location::RegisterLocation(count), slow_path, src_base);
Mark Mendell6bc53a92015-07-01 14:26:52 -0400937
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +0100938 // Validity checks: dest. Use src_base as a temporary register.
939 CheckPosition(assembler, destPos, dest, Location::RegisterLocation(count), slow_path, src_base);
Mark Mendell6bc53a92015-07-01 14:26:52 -0400940
941 // Okay, everything checks out. Finally time to do the copy.
942 // Check assumption that sizeof(Char) is 2 (used in scaling below).
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100943 const size_t char_size = DataType::Size(DataType::Type::kUint16);
Mark Mendell6bc53a92015-07-01 14:26:52 -0400944 DCHECK_EQ(char_size, 2u);
945
946 const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
947
948 if (srcPos.IsConstant()) {
949 int32_t srcPos_const = srcPos.GetConstant()->AsIntConstant()->GetValue();
950 __ leal(src_base, Address(src, char_size * srcPos_const + data_offset));
951 } else {
952 __ leal(src_base, Address(src, srcPos.AsRegister<Register>(),
953 ScaleFactor::TIMES_2, data_offset));
954 }
955 if (destPos.IsConstant()) {
956 int32_t destPos_const = destPos.GetConstant()->AsIntConstant()->GetValue();
957
958 __ leal(dest_base, Address(dest, char_size * destPos_const + data_offset));
959 } else {
960 __ leal(dest_base, Address(dest, destPos.AsRegister<Register>(),
961 ScaleFactor::TIMES_2, data_offset));
962 }
963
964 // Do the move.
965 __ rep_movsw();
966
967 __ Bind(slow_path->GetExitLabel());
968}
969
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +0000970void IntrinsicLocationsBuilderX86::VisitStringCompareTo(HInvoke* invoke) {
971 // The inputs plus one temp.
Vladimir Markoca6fff82017-10-03 14:49:14 +0100972 LocationSummary* locations = new (allocator_) LocationSummary(
973 invoke, LocationSummary::kCallOnMainAndSlowPath, kIntrinsified);
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +0000974 InvokeRuntimeCallingConvention calling_convention;
975 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
976 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
977 locations->SetOut(Location::RegisterLocation(EAX));
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +0000978}
979
980void IntrinsicCodeGeneratorX86::VisitStringCompareTo(HInvoke* invoke) {
981 X86Assembler* assembler = GetAssembler();
982 LocationSummary* locations = invoke->GetLocations();
983
Nicolas Geoffray512e04d2015-03-27 17:21:24 +0000984 // Note that the null check must have been done earlier.
Calin Juravle641547a2015-04-21 22:08:51 +0100985 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +0000986
987 Register argument = locations->InAt(1).AsRegister<Register>();
988 __ testl(argument, argument);
Vladimir Marko174b2e22017-10-12 13:34:49 +0100989 SlowPathCode* slow_path = new (codegen_->GetScopedAllocator()) IntrinsicSlowPathX86(invoke);
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +0000990 codegen_->AddSlowPath(slow_path);
991 __ j(kEqual, slow_path->GetEntryLabel());
992
Serban Constantinescuba45db02016-07-12 22:53:02 +0100993 codegen_->InvokeRuntime(kQuickStringCompareTo, invoke, invoke->GetDexPc(), slow_path);
Nicolas Geoffrayd75948a2015-03-27 09:53:16 +0000994 __ Bind(slow_path->GetExitLabel());
995}
996
Agi Csakid7138c82015-08-13 17:46:44 -0700997void IntrinsicLocationsBuilderX86::VisitStringEquals(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +0100998 LocationSummary* locations =
999 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Agi Csakid7138c82015-08-13 17:46:44 -07001000 locations->SetInAt(0, Location::RequiresRegister());
1001 locations->SetInAt(1, Location::RequiresRegister());
1002
1003 // Request temporary registers, ECX and EDI needed for repe_cmpsl instruction.
1004 locations->AddTemp(Location::RegisterLocation(ECX));
1005 locations->AddTemp(Location::RegisterLocation(EDI));
1006
1007 // Set output, ESI needed for repe_cmpsl instruction anyways.
1008 locations->SetOut(Location::RegisterLocation(ESI), Location::kOutputOverlap);
1009}
1010
1011void IntrinsicCodeGeneratorX86::VisitStringEquals(HInvoke* invoke) {
1012 X86Assembler* assembler = GetAssembler();
1013 LocationSummary* locations = invoke->GetLocations();
1014
1015 Register str = locations->InAt(0).AsRegister<Register>();
1016 Register arg = locations->InAt(1).AsRegister<Register>();
1017 Register ecx = locations->GetTemp(0).AsRegister<Register>();
1018 Register edi = locations->GetTemp(1).AsRegister<Register>();
1019 Register esi = locations->Out().AsRegister<Register>();
1020
Mark Mendell0c9497d2015-08-21 09:30:05 -04001021 NearLabel end, return_true, return_false;
Agi Csakid7138c82015-08-13 17:46:44 -07001022
1023 // Get offsets of count, value, and class fields within a string object.
1024 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
1025 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
1026 const uint32_t class_offset = mirror::Object::ClassOffset().Uint32Value();
1027
1028 // Note that the null check must have been done earlier.
1029 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1030
Nicolas Geoffraya83a54d2015-10-02 17:30:26 +01001031 StringEqualsOptimizations optimizations(invoke);
1032 if (!optimizations.GetArgumentNotNull()) {
1033 // Check if input is null, return false if it is.
1034 __ testl(arg, arg);
1035 __ j(kEqual, &return_false);
1036 }
Agi Csakid7138c82015-08-13 17:46:44 -07001037
Nicolas Geoffraya83a54d2015-10-02 17:30:26 +01001038 if (!optimizations.GetArgumentIsString()) {
Vladimir Marko53b52002016-05-24 19:30:45 +01001039 // Instanceof check for the argument by comparing class fields.
1040 // All string objects must have the same type since String cannot be subclassed.
1041 // Receiver must be a string object, so its class field is equal to all strings' class fields.
1042 // If the argument is a string object, its class field must be equal to receiver's class field.
Roland Levillain1d775d22018-09-07 13:56:57 +01001043 //
1044 // As the String class is expected to be non-movable, we can read the class
1045 // field from String.equals' arguments without read barriers.
1046 AssertNonMovableStringClass();
1047 // Also, because we use the loaded class references only to compare them, we
1048 // don't need to unpoison them.
1049 // /* HeapReference<Class> */ ecx = str->klass_
Nicolas Geoffraya83a54d2015-10-02 17:30:26 +01001050 __ movl(ecx, Address(str, class_offset));
Roland Levillain1d775d22018-09-07 13:56:57 +01001051 // if (ecx != /* HeapReference<Class> */ arg->klass_) return false
Nicolas Geoffraya83a54d2015-10-02 17:30:26 +01001052 __ cmpl(ecx, Address(arg, class_offset));
1053 __ j(kNotEqual, &return_false);
1054 }
Agi Csakid7138c82015-08-13 17:46:44 -07001055
1056 // Reference equality check, return true if same reference.
1057 __ cmpl(str, arg);
1058 __ j(kEqual, &return_true);
1059
jessicahandojo4877b792016-09-08 19:49:13 -07001060 // Load length and compression flag of receiver string.
Agi Csakid7138c82015-08-13 17:46:44 -07001061 __ movl(ecx, Address(str, count_offset));
jessicahandojo4877b792016-09-08 19:49:13 -07001062 // Check if lengths and compression flags are equal, return false if they're not.
1063 // Two identical strings will always have same compression style since
1064 // compression style is decided on alloc.
Agi Csakid7138c82015-08-13 17:46:44 -07001065 __ cmpl(ecx, Address(arg, count_offset));
1066 __ j(kNotEqual, &return_false);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001067 // Return true if strings are empty. Even with string compression `count == 0` means empty.
1068 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1069 "Expecting 0=compressed, 1=uncompressed");
1070 __ jecxz(&return_true);
Agi Csakid7138c82015-08-13 17:46:44 -07001071
jessicahandojo4877b792016-09-08 19:49:13 -07001072 if (mirror::kUseStringCompression) {
1073 NearLabel string_uncompressed;
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001074 // Extract length and differentiate between both compressed or both uncompressed.
1075 // Different compression style is cut above.
1076 __ shrl(ecx, Immediate(1));
1077 __ j(kCarrySet, &string_uncompressed);
jessicahandojo4877b792016-09-08 19:49:13 -07001078 // Divide string length by 2, rounding up, and continue as if uncompressed.
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001079 __ addl(ecx, Immediate(1));
jessicahandojo4877b792016-09-08 19:49:13 -07001080 __ shrl(ecx, Immediate(1));
1081 __ Bind(&string_uncompressed);
1082 }
Agi Csakid7138c82015-08-13 17:46:44 -07001083 // Load starting addresses of string values into ESI/EDI as required for repe_cmpsl instruction.
1084 __ leal(esi, Address(str, value_offset));
1085 __ leal(edi, Address(arg, value_offset));
1086
jessicahandojo4877b792016-09-08 19:49:13 -07001087 // Divide string length by 2 to compare characters 2 at a time and adjust for lengths not
1088 // divisible by 2.
Agi Csakid7138c82015-08-13 17:46:44 -07001089 __ addl(ecx, Immediate(1));
1090 __ shrl(ecx, Immediate(1));
1091
jessicahandojo4877b792016-09-08 19:49:13 -07001092 // Assertions that must hold in order to compare strings 2 characters (uncompressed)
1093 // or 4 characters (compressed) at a time.
Agi Csakid7138c82015-08-13 17:46:44 -07001094 DCHECK_ALIGNED(value_offset, 4);
1095 static_assert(IsAligned<4>(kObjectAlignment), "String of odd length is not zero padded");
1096
1097 // Loop to compare strings two characters at a time starting at the beginning of the string.
1098 __ repe_cmpsl();
1099 // If strings are not equal, zero flag will be cleared.
1100 __ j(kNotEqual, &return_false);
1101
1102 // Return true and exit the function.
1103 // If loop does not result in returning false, we return true.
1104 __ Bind(&return_true);
1105 __ movl(esi, Immediate(1));
1106 __ jmp(&end);
1107
1108 // Return false and exit the function.
1109 __ Bind(&return_false);
1110 __ xorl(esi, esi);
1111 __ Bind(&end);
1112}
1113
Andreas Gampe21030dd2015-05-07 14:46:15 -07001114static void CreateStringIndexOfLocations(HInvoke* invoke,
1115 ArenaAllocator* allocator,
1116 bool start_at_zero) {
1117 LocationSummary* locations = new (allocator) LocationSummary(invoke,
1118 LocationSummary::kCallOnSlowPath,
1119 kIntrinsified);
1120 // The data needs to be in EDI for scasw. So request that the string is there, anyways.
1121 locations->SetInAt(0, Location::RegisterLocation(EDI));
1122 // If we look for a constant char, we'll still have to copy it into EAX. So just request the
1123 // allocator to do that, anyways. We can still do the constant check by checking the parameter
1124 // of the instruction explicitly.
1125 // Note: This works as we don't clobber EAX anywhere.
1126 locations->SetInAt(1, Location::RegisterLocation(EAX));
1127 if (!start_at_zero) {
1128 locations->SetInAt(2, Location::RequiresRegister()); // The starting index.
1129 }
1130 // As we clobber EDI during execution anyways, also use it as the output.
1131 locations->SetOut(Location::SameAsFirstInput());
1132
1133 // repne scasw uses ECX as the counter.
1134 locations->AddTemp(Location::RegisterLocation(ECX));
1135 // Need another temporary to be able to compute the result.
1136 locations->AddTemp(Location::RequiresRegister());
jessicahandojo4877b792016-09-08 19:49:13 -07001137 if (mirror::kUseStringCompression) {
1138 // Need another temporary to be able to save unflagged string length.
1139 locations->AddTemp(Location::RequiresRegister());
1140 }
Andreas Gampe21030dd2015-05-07 14:46:15 -07001141}
1142
1143static void GenerateStringIndexOf(HInvoke* invoke,
1144 X86Assembler* assembler,
1145 CodeGeneratorX86* codegen,
Andreas Gampe21030dd2015-05-07 14:46:15 -07001146 bool start_at_zero) {
1147 LocationSummary* locations = invoke->GetLocations();
1148
1149 // Note that the null check must have been done earlier.
1150 DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
1151
1152 Register string_obj = locations->InAt(0).AsRegister<Register>();
1153 Register search_value = locations->InAt(1).AsRegister<Register>();
1154 Register counter = locations->GetTemp(0).AsRegister<Register>();
1155 Register string_length = locations->GetTemp(1).AsRegister<Register>();
1156 Register out = locations->Out().AsRegister<Register>();
jessicahandojo4877b792016-09-08 19:49:13 -07001157 // Only used when string compression feature is on.
1158 Register string_length_flagged;
Andreas Gampe21030dd2015-05-07 14:46:15 -07001159
1160 // Check our assumptions for registers.
1161 DCHECK_EQ(string_obj, EDI);
1162 DCHECK_EQ(search_value, EAX);
1163 DCHECK_EQ(counter, ECX);
1164 DCHECK_EQ(out, EDI);
1165
1166 // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically,
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001167 // or directly dispatch for a large constant, or omit slow-path for a small constant or a char.
Andreas Gampe85b62f22015-09-09 13:15:38 -07001168 SlowPathCode* slow_path = nullptr;
Vladimir Markofb6c90a2016-05-06 15:52:12 +01001169 HInstruction* code_point = invoke->InputAt(1);
1170 if (code_point->IsIntConstant()) {
Vladimir Markoda051082016-05-17 16:10:20 +01001171 if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) >
Andreas Gampe21030dd2015-05-07 14:46:15 -07001172 std::numeric_limits<uint16_t>::max()) {
1173 // Always needs the slow-path. We could directly dispatch to it, but this case should be
1174 // rare, so for simplicity just put the full slow-path down and branch unconditionally.
Vladimir Marko174b2e22017-10-12 13:34:49 +01001175 slow_path = new (codegen->GetScopedAllocator()) IntrinsicSlowPathX86(invoke);
Andreas Gampe21030dd2015-05-07 14:46:15 -07001176 codegen->AddSlowPath(slow_path);
1177 __ jmp(slow_path->GetEntryLabel());
1178 __ Bind(slow_path->GetExitLabel());
1179 return;
1180 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001181 } else if (code_point->GetType() != DataType::Type::kUint16) {
Andreas Gampe21030dd2015-05-07 14:46:15 -07001182 __ cmpl(search_value, Immediate(std::numeric_limits<uint16_t>::max()));
Vladimir Marko174b2e22017-10-12 13:34:49 +01001183 slow_path = new (codegen->GetScopedAllocator()) IntrinsicSlowPathX86(invoke);
Andreas Gampe21030dd2015-05-07 14:46:15 -07001184 codegen->AddSlowPath(slow_path);
1185 __ j(kAbove, slow_path->GetEntryLabel());
1186 }
1187
1188 // From here down, we know that we are looking for a char that fits in 16 bits.
1189 // Location of reference to data array within the String object.
1190 int32_t value_offset = mirror::String::ValueOffset().Int32Value();
1191 // Location of count within the String object.
1192 int32_t count_offset = mirror::String::CountOffset().Int32Value();
1193
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001194 // Load the count field of the string containing the length and compression flag.
Andreas Gampe21030dd2015-05-07 14:46:15 -07001195 __ movl(string_length, Address(string_obj, count_offset));
1196
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001197 // Do a zero-length check. Even with string compression `count == 0` means empty.
1198 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1199 "Expecting 0=compressed, 1=uncompressed");
Andreas Gampe21030dd2015-05-07 14:46:15 -07001200 // TODO: Support jecxz.
Mark Mendell0c9497d2015-08-21 09:30:05 -04001201 NearLabel not_found_label;
Andreas Gampe21030dd2015-05-07 14:46:15 -07001202 __ testl(string_length, string_length);
1203 __ j(kEqual, &not_found_label);
1204
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001205 if (mirror::kUseStringCompression) {
1206 string_length_flagged = locations->GetTemp(2).AsRegister<Register>();
1207 __ movl(string_length_flagged, string_length);
1208 // Extract the length and shift out the least significant bit used as compression flag.
1209 __ shrl(string_length, Immediate(1));
1210 }
1211
Andreas Gampe21030dd2015-05-07 14:46:15 -07001212 if (start_at_zero) {
1213 // Number of chars to scan is the same as the string length.
1214 __ movl(counter, string_length);
1215
1216 // Move to the start of the string.
1217 __ addl(string_obj, Immediate(value_offset));
1218 } else {
1219 Register start_index = locations->InAt(2).AsRegister<Register>();
1220
1221 // Do a start_index check.
1222 __ cmpl(start_index, string_length);
1223 __ j(kGreaterEqual, &not_found_label);
1224
1225 // Ensure we have a start index >= 0;
1226 __ xorl(counter, counter);
1227 __ cmpl(start_index, Immediate(0));
1228 __ cmovl(kGreater, counter, start_index);
1229
jessicahandojo4877b792016-09-08 19:49:13 -07001230 if (mirror::kUseStringCompression) {
1231 NearLabel modify_counter, offset_uncompressed_label;
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001232 __ testl(string_length_flagged, Immediate(1));
1233 __ j(kNotZero, &offset_uncompressed_label);
jessicahandojo4877b792016-09-08 19:49:13 -07001234 // Move to the start of the string: string_obj + value_offset + start_index.
1235 __ leal(string_obj, Address(string_obj, counter, ScaleFactor::TIMES_1, value_offset));
1236 __ jmp(&modify_counter);
Andreas Gampe21030dd2015-05-07 14:46:15 -07001237
jessicahandojo4877b792016-09-08 19:49:13 -07001238 // Move to the start of the string: string_obj + value_offset + 2 * start_index.
1239 __ Bind(&offset_uncompressed_label);
1240 __ leal(string_obj, Address(string_obj, counter, ScaleFactor::TIMES_2, value_offset));
1241
1242 // Now update ecx (the repne scasw work counter). We have string.length - start_index left to
1243 // compare.
1244 __ Bind(&modify_counter);
1245 } else {
1246 __ leal(string_obj, Address(string_obj, counter, ScaleFactor::TIMES_2, value_offset));
1247 }
Andreas Gampe21030dd2015-05-07 14:46:15 -07001248 __ negl(counter);
1249 __ leal(counter, Address(string_length, counter, ScaleFactor::TIMES_1, 0));
1250 }
1251
jessicahandojo4877b792016-09-08 19:49:13 -07001252 if (mirror::kUseStringCompression) {
1253 NearLabel uncompressed_string_comparison;
1254 NearLabel comparison_done;
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001255 __ testl(string_length_flagged, Immediate(1));
1256 __ j(kNotZero, &uncompressed_string_comparison);
Andreas Gampe21030dd2015-05-07 14:46:15 -07001257
jessicahandojo4877b792016-09-08 19:49:13 -07001258 // Check if EAX (search_value) is ASCII.
1259 __ cmpl(search_value, Immediate(127));
1260 __ j(kGreater, &not_found_label);
1261 // Comparing byte-per-byte.
1262 __ repne_scasb();
1263 __ jmp(&comparison_done);
1264
1265 // Everything is set up for repne scasw:
1266 // * Comparison address in EDI.
1267 // * Counter in ECX.
1268 __ Bind(&uncompressed_string_comparison);
1269 __ repne_scasw();
1270 __ Bind(&comparison_done);
1271 } else {
1272 __ repne_scasw();
1273 }
Andreas Gampe21030dd2015-05-07 14:46:15 -07001274 // Did we find a match?
1275 __ j(kNotEqual, &not_found_label);
1276
1277 // Yes, we matched. Compute the index of the result.
1278 __ subl(string_length, counter);
1279 __ leal(out, Address(string_length, -1));
1280
Mark Mendell0c9497d2015-08-21 09:30:05 -04001281 NearLabel done;
Andreas Gampe21030dd2015-05-07 14:46:15 -07001282 __ jmp(&done);
1283
1284 // Failed to match; return -1.
1285 __ Bind(&not_found_label);
1286 __ movl(out, Immediate(-1));
1287
1288 // And join up at the end.
1289 __ Bind(&done);
1290 if (slow_path != nullptr) {
1291 __ Bind(slow_path->GetExitLabel());
1292 }
1293}
1294
1295void IntrinsicLocationsBuilderX86::VisitStringIndexOf(HInvoke* invoke) {
Andreas Gampe3db70682018-12-26 15:12:03 -08001296 CreateStringIndexOfLocations(invoke, allocator_, /* start_at_zero= */ true);
Andreas Gampe21030dd2015-05-07 14:46:15 -07001297}
1298
1299void IntrinsicCodeGeneratorX86::VisitStringIndexOf(HInvoke* invoke) {
Andreas Gampe3db70682018-12-26 15:12:03 -08001300 GenerateStringIndexOf(invoke, GetAssembler(), codegen_, /* start_at_zero= */ true);
Andreas Gampe21030dd2015-05-07 14:46:15 -07001301}
1302
1303void IntrinsicLocationsBuilderX86::VisitStringIndexOfAfter(HInvoke* invoke) {
Andreas Gampe3db70682018-12-26 15:12:03 -08001304 CreateStringIndexOfLocations(invoke, allocator_, /* start_at_zero= */ false);
Andreas Gampe21030dd2015-05-07 14:46:15 -07001305}
1306
1307void IntrinsicCodeGeneratorX86::VisitStringIndexOfAfter(HInvoke* invoke) {
Andreas Gampe3db70682018-12-26 15:12:03 -08001308 GenerateStringIndexOf(invoke, GetAssembler(), codegen_, /* start_at_zero= */ false);
Andreas Gampe21030dd2015-05-07 14:46:15 -07001309}
1310
Jeff Hao848f70a2014-01-15 13:49:50 -08001311void IntrinsicLocationsBuilderX86::VisitStringNewStringFromBytes(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001312 LocationSummary* locations = new (allocator_) LocationSummary(
1313 invoke, LocationSummary::kCallOnMainAndSlowPath, kIntrinsified);
Jeff Hao848f70a2014-01-15 13:49:50 -08001314 InvokeRuntimeCallingConvention calling_convention;
1315 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1316 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1317 locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
1318 locations->SetInAt(3, Location::RegisterLocation(calling_convention.GetRegisterAt(3)));
1319 locations->SetOut(Location::RegisterLocation(EAX));
Jeff Hao848f70a2014-01-15 13:49:50 -08001320}
1321
1322void IntrinsicCodeGeneratorX86::VisitStringNewStringFromBytes(HInvoke* invoke) {
1323 X86Assembler* assembler = GetAssembler();
1324 LocationSummary* locations = invoke->GetLocations();
1325
1326 Register byte_array = locations->InAt(0).AsRegister<Register>();
1327 __ testl(byte_array, byte_array);
Vladimir Marko174b2e22017-10-12 13:34:49 +01001328 SlowPathCode* slow_path = new (codegen_->GetScopedAllocator()) IntrinsicSlowPathX86(invoke);
Jeff Hao848f70a2014-01-15 13:49:50 -08001329 codegen_->AddSlowPath(slow_path);
1330 __ j(kEqual, slow_path->GetEntryLabel());
1331
Serban Constantinescuba45db02016-07-12 22:53:02 +01001332 codegen_->InvokeRuntime(kQuickAllocStringFromBytes, invoke, invoke->GetDexPc());
Roland Levillainf969a202016-03-09 16:14:00 +00001333 CheckEntrypointTypes<kQuickAllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001334 __ Bind(slow_path->GetExitLabel());
1335}
1336
1337void IntrinsicLocationsBuilderX86::VisitStringNewStringFromChars(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001338 LocationSummary* locations =
1339 new (allocator_) LocationSummary(invoke, LocationSummary::kCallOnMainOnly, kIntrinsified);
Jeff Hao848f70a2014-01-15 13:49:50 -08001340 InvokeRuntimeCallingConvention calling_convention;
1341 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1342 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
1343 locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
1344 locations->SetOut(Location::RegisterLocation(EAX));
1345}
1346
1347void IntrinsicCodeGeneratorX86::VisitStringNewStringFromChars(HInvoke* invoke) {
Roland Levillaincc3839c2016-02-29 16:23:48 +00001348 // No need to emit code checking whether `locations->InAt(2)` is a null
1349 // pointer, as callers of the native method
1350 //
1351 // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data)
1352 //
1353 // all include a null check on `data` before calling that method.
Serban Constantinescuba45db02016-07-12 22:53:02 +01001354 codegen_->InvokeRuntime(kQuickAllocStringFromChars, invoke, invoke->GetDexPc());
Roland Levillainf969a202016-03-09 16:14:00 +00001355 CheckEntrypointTypes<kQuickAllocStringFromChars, void*, int32_t, int32_t, void*>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001356}
1357
1358void IntrinsicLocationsBuilderX86::VisitStringNewStringFromString(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001359 LocationSummary* locations = new (allocator_) LocationSummary(
1360 invoke, LocationSummary::kCallOnMainAndSlowPath, kIntrinsified);
Jeff Hao848f70a2014-01-15 13:49:50 -08001361 InvokeRuntimeCallingConvention calling_convention;
1362 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
1363 locations->SetOut(Location::RegisterLocation(EAX));
Jeff Hao848f70a2014-01-15 13:49:50 -08001364}
1365
1366void IntrinsicCodeGeneratorX86::VisitStringNewStringFromString(HInvoke* invoke) {
1367 X86Assembler* assembler = GetAssembler();
1368 LocationSummary* locations = invoke->GetLocations();
1369
1370 Register string_to_copy = locations->InAt(0).AsRegister<Register>();
1371 __ testl(string_to_copy, string_to_copy);
Vladimir Marko174b2e22017-10-12 13:34:49 +01001372 SlowPathCode* slow_path = new (codegen_->GetScopedAllocator()) IntrinsicSlowPathX86(invoke);
Jeff Hao848f70a2014-01-15 13:49:50 -08001373 codegen_->AddSlowPath(slow_path);
1374 __ j(kEqual, slow_path->GetEntryLabel());
1375
Serban Constantinescuba45db02016-07-12 22:53:02 +01001376 codegen_->InvokeRuntime(kQuickAllocStringFromString, invoke, invoke->GetDexPc());
Roland Levillainf969a202016-03-09 16:14:00 +00001377 CheckEntrypointTypes<kQuickAllocStringFromString, void*, void*>();
Jeff Hao848f70a2014-01-15 13:49:50 -08001378 __ Bind(slow_path->GetExitLabel());
1379}
1380
Mark Mendell8f8926a2015-08-17 11:39:06 -04001381void IntrinsicLocationsBuilderX86::VisitStringGetCharsNoCheck(HInvoke* invoke) {
1382 // public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin);
Vladimir Markoca6fff82017-10-03 14:49:14 +01001383 LocationSummary* locations =
1384 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Mark Mendell8f8926a2015-08-17 11:39:06 -04001385 locations->SetInAt(0, Location::RequiresRegister());
1386 locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
1387 // Place srcEnd in ECX to save a move below.
1388 locations->SetInAt(2, Location::RegisterLocation(ECX));
1389 locations->SetInAt(3, Location::RequiresRegister());
1390 locations->SetInAt(4, Location::RequiresRegister());
1391
1392 // And we need some temporaries. We will use REP MOVSW, so we need fixed registers.
1393 // We don't have enough registers to also grab ECX, so handle below.
1394 locations->AddTemp(Location::RegisterLocation(ESI));
1395 locations->AddTemp(Location::RegisterLocation(EDI));
1396}
1397
1398void IntrinsicCodeGeneratorX86::VisitStringGetCharsNoCheck(HInvoke* invoke) {
1399 X86Assembler* assembler = GetAssembler();
1400 LocationSummary* locations = invoke->GetLocations();
1401
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001402 size_t char_component_size = DataType::Size(DataType::Type::kUint16);
Mark Mendell8f8926a2015-08-17 11:39:06 -04001403 // Location of data in char array buffer.
1404 const uint32_t data_offset = mirror::Array::DataOffset(char_component_size).Uint32Value();
1405 // Location of char array data in string.
1406 const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
1407
1408 // public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin);
1409 Register obj = locations->InAt(0).AsRegister<Register>();
1410 Location srcBegin = locations->InAt(1);
1411 int srcBegin_value =
1412 srcBegin.IsConstant() ? srcBegin.GetConstant()->AsIntConstant()->GetValue() : 0;
1413 Register srcEnd = locations->InAt(2).AsRegister<Register>();
1414 Register dst = locations->InAt(3).AsRegister<Register>();
1415 Register dstBegin = locations->InAt(4).AsRegister<Register>();
1416
1417 // Check assumption that sizeof(Char) is 2 (used in scaling below).
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001418 const size_t char_size = DataType::Size(DataType::Type::kUint16);
Mark Mendell8f8926a2015-08-17 11:39:06 -04001419 DCHECK_EQ(char_size, 2u);
1420
Mark Mendell8f8926a2015-08-17 11:39:06 -04001421 // Compute the number of chars (words) to move.
jessicahandojo4877b792016-09-08 19:49:13 -07001422 // Save ECX, since we don't know if it will be used later.
Mark Mendell8f8926a2015-08-17 11:39:06 -04001423 __ pushl(ECX);
1424 int stack_adjust = kX86WordSize;
1425 __ cfi().AdjustCFAOffset(stack_adjust);
1426 DCHECK_EQ(srcEnd, ECX);
1427 if (srcBegin.IsConstant()) {
jessicahandojo4877b792016-09-08 19:49:13 -07001428 __ subl(ECX, Immediate(srcBegin_value));
Mark Mendell8f8926a2015-08-17 11:39:06 -04001429 } else {
1430 DCHECK(srcBegin.IsRegister());
1431 __ subl(ECX, srcBegin.AsRegister<Register>());
1432 }
1433
jessicahandojo4877b792016-09-08 19:49:13 -07001434 NearLabel done;
1435 if (mirror::kUseStringCompression) {
1436 // Location of count in string
1437 const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001438 const size_t c_char_size = DataType::Size(DataType::Type::kInt8);
jessicahandojo4877b792016-09-08 19:49:13 -07001439 DCHECK_EQ(c_char_size, 1u);
1440 __ pushl(EAX);
1441 __ cfi().AdjustCFAOffset(stack_adjust);
1442
1443 NearLabel copy_loop, copy_uncompressed;
Vladimir Markofdaf0f42016-10-13 19:29:53 +01001444 __ testl(Address(obj, count_offset), Immediate(1));
1445 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
1446 "Expecting 0=compressed, 1=uncompressed");
1447 __ j(kNotZero, &copy_uncompressed);
jessicahandojo4877b792016-09-08 19:49:13 -07001448 // Compute the address of the source string by adding the number of chars from
1449 // the source beginning to the value offset of a string.
1450 __ leal(ESI, CodeGeneratorX86::ArrayAddress(obj, srcBegin, TIMES_1, value_offset));
1451
1452 // Start the loop to copy String's value to Array of Char.
1453 __ leal(EDI, Address(dst, dstBegin, ScaleFactor::TIMES_2, data_offset));
1454 __ Bind(&copy_loop);
1455 __ jecxz(&done);
1456 // Use EAX temporary (convert byte from ESI to word).
1457 // TODO: Use LODSB/STOSW (not supported by X86Assembler) with AH initialized to 0.
1458 __ movzxb(EAX, Address(ESI, 0));
1459 __ movw(Address(EDI, 0), EAX);
1460 __ leal(EDI, Address(EDI, char_size));
1461 __ leal(ESI, Address(ESI, c_char_size));
1462 // TODO: Add support for LOOP to X86Assembler.
1463 __ subl(ECX, Immediate(1));
1464 __ jmp(&copy_loop);
1465 __ Bind(&copy_uncompressed);
1466 }
1467
1468 // Do the copy for uncompressed string.
1469 // Compute the address of the destination buffer.
1470 __ leal(EDI, Address(dst, dstBegin, ScaleFactor::TIMES_2, data_offset));
1471 __ leal(ESI, CodeGeneratorX86::ArrayAddress(obj, srcBegin, TIMES_2, value_offset));
Mark Mendell8f8926a2015-08-17 11:39:06 -04001472 __ rep_movsw();
1473
jessicahandojo4877b792016-09-08 19:49:13 -07001474 __ Bind(&done);
1475 if (mirror::kUseStringCompression) {
1476 // Restore EAX.
1477 __ popl(EAX);
1478 __ cfi().AdjustCFAOffset(-stack_adjust);
1479 }
1480 // Restore ECX.
Mark Mendell8f8926a2015-08-17 11:39:06 -04001481 __ popl(ECX);
1482 __ cfi().AdjustCFAOffset(-stack_adjust);
1483}
1484
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001485static void GenPeek(LocationSummary* locations, DataType::Type size, X86Assembler* assembler) {
Mark Mendell09ed1a32015-03-25 08:30:06 -04001486 Register address = locations->InAt(0).AsRegisterPairLow<Register>();
1487 Location out_loc = locations->Out();
1488 // x86 allows unaligned access. We do not have to check the input or use specific instructions
1489 // to avoid a SIGBUS.
1490 switch (size) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001491 case DataType::Type::kInt8:
Mark Mendell09ed1a32015-03-25 08:30:06 -04001492 __ movsxb(out_loc.AsRegister<Register>(), Address(address, 0));
1493 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001494 case DataType::Type::kInt16:
Mark Mendell09ed1a32015-03-25 08:30:06 -04001495 __ movsxw(out_loc.AsRegister<Register>(), Address(address, 0));
1496 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001497 case DataType::Type::kInt32:
Mark Mendell09ed1a32015-03-25 08:30:06 -04001498 __ movl(out_loc.AsRegister<Register>(), Address(address, 0));
1499 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001500 case DataType::Type::kInt64:
Mark Mendell09ed1a32015-03-25 08:30:06 -04001501 __ movl(out_loc.AsRegisterPairLow<Register>(), Address(address, 0));
1502 __ movl(out_loc.AsRegisterPairHigh<Register>(), Address(address, 4));
1503 break;
1504 default:
1505 LOG(FATAL) << "Type not recognized for peek: " << size;
1506 UNREACHABLE();
1507 }
1508}
1509
1510void IntrinsicLocationsBuilderX86::VisitMemoryPeekByte(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001511 CreateLongToIntLocations(allocator_, invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001512}
1513
1514void IntrinsicCodeGeneratorX86::VisitMemoryPeekByte(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001515 GenPeek(invoke->GetLocations(), DataType::Type::kInt8, GetAssembler());
Mark Mendell09ed1a32015-03-25 08:30:06 -04001516}
1517
1518void IntrinsicLocationsBuilderX86::VisitMemoryPeekIntNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001519 CreateLongToIntLocations(allocator_, invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001520}
1521
1522void IntrinsicCodeGeneratorX86::VisitMemoryPeekIntNative(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001523 GenPeek(invoke->GetLocations(), DataType::Type::kInt32, GetAssembler());
Mark Mendell09ed1a32015-03-25 08:30:06 -04001524}
1525
1526void IntrinsicLocationsBuilderX86::VisitMemoryPeekLongNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001527 CreateLongToLongLocations(allocator_, invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001528}
1529
1530void IntrinsicCodeGeneratorX86::VisitMemoryPeekLongNative(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001531 GenPeek(invoke->GetLocations(), DataType::Type::kInt64, GetAssembler());
Mark Mendell09ed1a32015-03-25 08:30:06 -04001532}
1533
1534void IntrinsicLocationsBuilderX86::VisitMemoryPeekShortNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001535 CreateLongToIntLocations(allocator_, invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001536}
1537
1538void IntrinsicCodeGeneratorX86::VisitMemoryPeekShortNative(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001539 GenPeek(invoke->GetLocations(), DataType::Type::kInt16, GetAssembler());
Mark Mendell09ed1a32015-03-25 08:30:06 -04001540}
1541
Vladimir Markoca6fff82017-10-03 14:49:14 +01001542static void CreateLongIntToVoidLocations(ArenaAllocator* allocator,
1543 DataType::Type size,
Mark Mendell09ed1a32015-03-25 08:30:06 -04001544 HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001545 LocationSummary* locations =
1546 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001547 locations->SetInAt(0, Location::RequiresRegister());
Roland Levillain4c0eb422015-04-24 16:43:49 +01001548 HInstruction* value = invoke->InputAt(1);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001549 if (size == DataType::Type::kInt8) {
Mark Mendell09ed1a32015-03-25 08:30:06 -04001550 locations->SetInAt(1, Location::ByteRegisterOrConstant(EDX, value));
1551 } else {
1552 locations->SetInAt(1, Location::RegisterOrConstant(value));
1553 }
1554}
1555
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001556static void GenPoke(LocationSummary* locations, DataType::Type size, X86Assembler* assembler) {
Mark Mendell09ed1a32015-03-25 08:30:06 -04001557 Register address = locations->InAt(0).AsRegisterPairLow<Register>();
1558 Location value_loc = locations->InAt(1);
1559 // x86 allows unaligned access. We do not have to check the input or use specific instructions
1560 // to avoid a SIGBUS.
1561 switch (size) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001562 case DataType::Type::kInt8:
Mark Mendell09ed1a32015-03-25 08:30:06 -04001563 if (value_loc.IsConstant()) {
1564 __ movb(Address(address, 0),
1565 Immediate(value_loc.GetConstant()->AsIntConstant()->GetValue()));
1566 } else {
1567 __ movb(Address(address, 0), value_loc.AsRegister<ByteRegister>());
1568 }
1569 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001570 case DataType::Type::kInt16:
Mark Mendell09ed1a32015-03-25 08:30:06 -04001571 if (value_loc.IsConstant()) {
1572 __ movw(Address(address, 0),
1573 Immediate(value_loc.GetConstant()->AsIntConstant()->GetValue()));
1574 } else {
1575 __ movw(Address(address, 0), value_loc.AsRegister<Register>());
1576 }
1577 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001578 case DataType::Type::kInt32:
Mark Mendell09ed1a32015-03-25 08:30:06 -04001579 if (value_loc.IsConstant()) {
1580 __ movl(Address(address, 0),
1581 Immediate(value_loc.GetConstant()->AsIntConstant()->GetValue()));
1582 } else {
1583 __ movl(Address(address, 0), value_loc.AsRegister<Register>());
1584 }
1585 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001586 case DataType::Type::kInt64:
Mark Mendell09ed1a32015-03-25 08:30:06 -04001587 if (value_loc.IsConstant()) {
1588 int64_t value = value_loc.GetConstant()->AsLongConstant()->GetValue();
1589 __ movl(Address(address, 0), Immediate(Low32Bits(value)));
1590 __ movl(Address(address, 4), Immediate(High32Bits(value)));
1591 } else {
1592 __ movl(Address(address, 0), value_loc.AsRegisterPairLow<Register>());
1593 __ movl(Address(address, 4), value_loc.AsRegisterPairHigh<Register>());
1594 }
1595 break;
1596 default:
1597 LOG(FATAL) << "Type not recognized for poke: " << size;
1598 UNREACHABLE();
1599 }
1600}
1601
1602void IntrinsicLocationsBuilderX86::VisitMemoryPokeByte(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001603 CreateLongIntToVoidLocations(allocator_, DataType::Type::kInt8, invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001604}
1605
1606void IntrinsicCodeGeneratorX86::VisitMemoryPokeByte(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001607 GenPoke(invoke->GetLocations(), DataType::Type::kInt8, GetAssembler());
Mark Mendell09ed1a32015-03-25 08:30:06 -04001608}
1609
1610void IntrinsicLocationsBuilderX86::VisitMemoryPokeIntNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001611 CreateLongIntToVoidLocations(allocator_, DataType::Type::kInt32, invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001612}
1613
1614void IntrinsicCodeGeneratorX86::VisitMemoryPokeIntNative(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001615 GenPoke(invoke->GetLocations(), DataType::Type::kInt32, GetAssembler());
Mark Mendell09ed1a32015-03-25 08:30:06 -04001616}
1617
1618void IntrinsicLocationsBuilderX86::VisitMemoryPokeLongNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001619 CreateLongIntToVoidLocations(allocator_, DataType::Type::kInt64, invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001620}
1621
1622void IntrinsicCodeGeneratorX86::VisitMemoryPokeLongNative(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001623 GenPoke(invoke->GetLocations(), DataType::Type::kInt64, GetAssembler());
Mark Mendell09ed1a32015-03-25 08:30:06 -04001624}
1625
1626void IntrinsicLocationsBuilderX86::VisitMemoryPokeShortNative(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001627 CreateLongIntToVoidLocations(allocator_, DataType::Type::kInt16, invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001628}
1629
1630void IntrinsicCodeGeneratorX86::VisitMemoryPokeShortNative(HInvoke* invoke) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001631 GenPoke(invoke->GetLocations(), DataType::Type::kInt16, GetAssembler());
Mark Mendell09ed1a32015-03-25 08:30:06 -04001632}
1633
1634void IntrinsicLocationsBuilderX86::VisitThreadCurrentThread(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001635 LocationSummary* locations =
1636 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001637 locations->SetOut(Location::RequiresRegister());
1638}
1639
1640void IntrinsicCodeGeneratorX86::VisitThreadCurrentThread(HInvoke* invoke) {
1641 Register out = invoke->GetLocations()->Out().AsRegister<Register>();
Andreas Gampe542451c2016-07-26 09:02:02 -07001642 GetAssembler()->fs()->movl(out, Address::Absolute(Thread::PeerOffset<kX86PointerSize>()));
Mark Mendell09ed1a32015-03-25 08:30:06 -04001643}
1644
Roland Levillain0d5a2812015-11-13 10:07:31 +00001645static void GenUnsafeGet(HInvoke* invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001646 DataType::Type type,
Roland Levillain0d5a2812015-11-13 10:07:31 +00001647 bool is_volatile,
1648 CodeGeneratorX86* codegen) {
1649 X86Assembler* assembler = down_cast<X86Assembler*>(codegen->GetAssembler());
1650 LocationSummary* locations = invoke->GetLocations();
1651 Location base_loc = locations->InAt(1);
1652 Register base = base_loc.AsRegister<Register>();
1653 Location offset_loc = locations->InAt(2);
1654 Register offset = offset_loc.AsRegisterPairLow<Register>();
1655 Location output_loc = locations->Out();
Mark Mendell09ed1a32015-03-25 08:30:06 -04001656
1657 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001658 case DataType::Type::kInt32: {
Roland Levillain0d5a2812015-11-13 10:07:31 +00001659 Register output = output_loc.AsRegister<Register>();
1660 __ movl(output, Address(base, offset, ScaleFactor::TIMES_1, 0));
Roland Levillain7c1559a2015-12-15 10:55:36 +00001661 break;
1662 }
1663
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001664 case DataType::Type::kReference: {
Roland Levillain7c1559a2015-12-15 10:55:36 +00001665 Register output = output_loc.AsRegister<Register>();
1666 if (kEmitCompilerReadBarrier) {
1667 if (kUseBakerReadBarrier) {
Sang, Chunlei0fcd2b82016-04-05 17:12:59 +08001668 Address src(base, offset, ScaleFactor::TIMES_1, 0);
1669 codegen->GenerateReferenceLoadWithBakerReadBarrier(
Andreas Gampe3db70682018-12-26 15:12:03 -08001670 invoke, output_loc, base, src, /* needs_null_check= */ false);
Roland Levillain7c1559a2015-12-15 10:55:36 +00001671 } else {
1672 __ movl(output, Address(base, offset, ScaleFactor::TIMES_1, 0));
1673 codegen->GenerateReadBarrierSlow(
1674 invoke, output_loc, output_loc, base_loc, 0U, offset_loc);
1675 }
1676 } else {
1677 __ movl(output, Address(base, offset, ScaleFactor::TIMES_1, 0));
1678 __ MaybeUnpoisonHeapReference(output);
Roland Levillain4d027112015-07-01 15:41:14 +01001679 }
Mark Mendell09ed1a32015-03-25 08:30:06 -04001680 break;
Roland Levillain4d027112015-07-01 15:41:14 +01001681 }
Mark Mendell09ed1a32015-03-25 08:30:06 -04001682
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001683 case DataType::Type::kInt64: {
Roland Levillain0d5a2812015-11-13 10:07:31 +00001684 Register output_lo = output_loc.AsRegisterPairLow<Register>();
1685 Register output_hi = output_loc.AsRegisterPairHigh<Register>();
Mark Mendell09ed1a32015-03-25 08:30:06 -04001686 if (is_volatile) {
1687 // Need to use a XMM to read atomically.
1688 XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
1689 __ movsd(temp, Address(base, offset, ScaleFactor::TIMES_1, 0));
1690 __ movd(output_lo, temp);
1691 __ psrlq(temp, Immediate(32));
1692 __ movd(output_hi, temp);
1693 } else {
1694 __ movl(output_lo, Address(base, offset, ScaleFactor::TIMES_1, 0));
1695 __ movl(output_hi, Address(base, offset, ScaleFactor::TIMES_1, 4));
1696 }
1697 }
1698 break;
1699
1700 default:
1701 LOG(FATAL) << "Unsupported op size " << type;
1702 UNREACHABLE();
1703 }
1704}
1705
Sorin Basca507cf902021-10-06 12:04:56 +00001706static bool UnsafeGetIntrinsicOnCallList(Intrinsics intrinsic) {
1707 switch (intrinsic) {
1708 case Intrinsics::kUnsafeGetObject:
1709 case Intrinsics::kUnsafeGetObjectVolatile:
1710 case Intrinsics::kJdkUnsafeGetObject:
1711 case Intrinsics::kJdkUnsafeGetObjectVolatile:
1712 case Intrinsics::kJdkUnsafeGetObjectAcquire:
1713 return true;
1714 default:
1715 break;
1716 }
1717 return false;
1718}
1719
Vladimir Markoca6fff82017-10-03 14:49:14 +01001720static void CreateIntIntIntToIntLocations(ArenaAllocator* allocator,
Roland Levillain7c1559a2015-12-15 10:55:36 +00001721 HInvoke* invoke,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001722 DataType::Type type,
Roland Levillain7c1559a2015-12-15 10:55:36 +00001723 bool is_volatile) {
Sorin Basca507cf902021-10-06 12:04:56 +00001724 bool can_call = kEmitCompilerReadBarrier && UnsafeGetIntrinsicOnCallList(invoke->GetIntrinsic());
Vladimir Markoca6fff82017-10-03 14:49:14 +01001725 LocationSummary* locations =
1726 new (allocator) LocationSummary(invoke,
1727 can_call
1728 ? LocationSummary::kCallOnSlowPath
1729 : LocationSummary::kNoCall,
1730 kIntrinsified);
Vladimir Marko70e97462016-08-09 11:04:26 +01001731 if (can_call && kUseBakerReadBarrier) {
Vladimir Marko804b03f2016-09-14 16:26:36 +01001732 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Vladimir Marko70e97462016-08-09 11:04:26 +01001733 }
Mark Mendell09ed1a32015-03-25 08:30:06 -04001734 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
1735 locations->SetInAt(1, Location::RequiresRegister());
1736 locations->SetInAt(2, Location::RequiresRegister());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001737 if (type == DataType::Type::kInt64) {
Mark Mendell09ed1a32015-03-25 08:30:06 -04001738 if (is_volatile) {
1739 // Need to use XMM to read volatile.
1740 locations->AddTemp(Location::RequiresFpuRegister());
Roland Levillain3d312422016-06-23 13:53:42 +01001741 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001742 } else {
1743 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1744 }
1745 } else {
Roland Levillain3d312422016-06-23 13:53:42 +01001746 locations->SetOut(Location::RequiresRegister(),
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001747 (can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap));
Mark Mendell09ed1a32015-03-25 08:30:06 -04001748 }
1749}
1750
1751void IntrinsicLocationsBuilderX86::VisitUnsafeGet(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001752 VisitJdkUnsafeGet(invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001753}
1754void IntrinsicLocationsBuilderX86::VisitUnsafeGetVolatile(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001755 VisitJdkUnsafeGetVolatile(invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001756}
1757void IntrinsicLocationsBuilderX86::VisitUnsafeGetLong(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001758 VisitJdkUnsafeGetLong(invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001759}
1760void IntrinsicLocationsBuilderX86::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001761 VisitJdkUnsafeGetLongVolatile(invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001762}
1763void IntrinsicLocationsBuilderX86::VisitUnsafeGetObject(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001764 VisitJdkUnsafeGetObject(invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001765}
1766void IntrinsicLocationsBuilderX86::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001767 VisitJdkUnsafeGetObjectVolatile(invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001768}
1769
1770
1771void IntrinsicCodeGeneratorX86::VisitUnsafeGet(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001772 VisitJdkUnsafeGet(invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001773}
1774void IntrinsicCodeGeneratorX86::VisitUnsafeGetVolatile(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001775 VisitJdkUnsafeGetVolatile(invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001776}
1777void IntrinsicCodeGeneratorX86::VisitUnsafeGetLong(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001778 VisitJdkUnsafeGetLong(invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001779}
1780void IntrinsicCodeGeneratorX86::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001781 VisitJdkUnsafeGetLongVolatile(invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001782}
1783void IntrinsicCodeGeneratorX86::VisitUnsafeGetObject(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001784 VisitJdkUnsafeGetObject(invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001785}
1786void IntrinsicCodeGeneratorX86::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001787 VisitJdkUnsafeGetObjectVolatile(invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001788}
1789
1790
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001791void IntrinsicLocationsBuilderX86::VisitJdkUnsafeGet(HInvoke* invoke) {
1792 CreateIntIntIntToIntLocations(
1793 allocator_, invoke, DataType::Type::kInt32, /*is_volatile=*/ false);
1794}
1795void IntrinsicLocationsBuilderX86::VisitJdkUnsafeGetVolatile(HInvoke* invoke) {
1796 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kInt32, /*is_volatile=*/ true);
1797}
Sorin Basca0069ad72021-09-17 17:33:09 +00001798void IntrinsicLocationsBuilderX86::VisitJdkUnsafeGetAcquire(HInvoke* invoke) {
1799 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kInt32, /*is_volatile=*/ true);
1800}
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001801void IntrinsicLocationsBuilderX86::VisitJdkUnsafeGetLong(HInvoke* invoke) {
1802 CreateIntIntIntToIntLocations(
1803 allocator_, invoke, DataType::Type::kInt64, /*is_volatile=*/ false);
1804}
1805void IntrinsicLocationsBuilderX86::VisitJdkUnsafeGetLongVolatile(HInvoke* invoke) {
1806 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kInt64, /*is_volatile=*/ true);
1807}
Sorin Basca507cf902021-10-06 12:04:56 +00001808void IntrinsicLocationsBuilderX86::VisitJdkUnsafeGetLongAcquire(HInvoke* invoke) {
1809 CreateIntIntIntToIntLocations(allocator_, invoke, DataType::Type::kInt64, /*is_volatile=*/ true);
1810}
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001811void IntrinsicLocationsBuilderX86::VisitJdkUnsafeGetObject(HInvoke* invoke) {
1812 CreateIntIntIntToIntLocations(
1813 allocator_, invoke, DataType::Type::kReference, /*is_volatile=*/ false);
1814}
1815void IntrinsicLocationsBuilderX86::VisitJdkUnsafeGetObjectVolatile(HInvoke* invoke) {
1816 CreateIntIntIntToIntLocations(
1817 allocator_, invoke, DataType::Type::kReference, /*is_volatile=*/ true);
1818}
Sorin Basca507cf902021-10-06 12:04:56 +00001819void IntrinsicLocationsBuilderX86::VisitJdkUnsafeGetObjectAcquire(HInvoke* invoke) {
1820 CreateIntIntIntToIntLocations(
1821 allocator_, invoke, DataType::Type::kReference, /*is_volatile=*/ true);
1822}
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001823
1824void IntrinsicCodeGeneratorX86::VisitJdkUnsafeGet(HInvoke* invoke) {
1825 GenUnsafeGet(invoke, DataType::Type::kInt32, /*is_volatile=*/ false, codegen_);
1826}
1827void IntrinsicCodeGeneratorX86::VisitJdkUnsafeGetVolatile(HInvoke* invoke) {
1828 GenUnsafeGet(invoke, DataType::Type::kInt32, /*is_volatile=*/ true, codegen_);
1829}
Sorin Basca0069ad72021-09-17 17:33:09 +00001830void IntrinsicCodeGeneratorX86::VisitJdkUnsafeGetAcquire(HInvoke* invoke) {
1831 GenUnsafeGet(invoke, DataType::Type::kInt32, /*is_volatile=*/ true, codegen_);
1832}
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001833void IntrinsicCodeGeneratorX86::VisitJdkUnsafeGetLong(HInvoke* invoke) {
1834 GenUnsafeGet(invoke, DataType::Type::kInt64, /*is_volatile=*/ false, codegen_);
1835}
1836void IntrinsicCodeGeneratorX86::VisitJdkUnsafeGetLongVolatile(HInvoke* invoke) {
1837 GenUnsafeGet(invoke, DataType::Type::kInt64, /*is_volatile=*/ true, codegen_);
1838}
Sorin Basca507cf902021-10-06 12:04:56 +00001839void IntrinsicCodeGeneratorX86::VisitJdkUnsafeGetLongAcquire(HInvoke* invoke) {
1840 GenUnsafeGet(invoke, DataType::Type::kInt64, /*is_volatile=*/ true, codegen_);
1841}
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001842void IntrinsicCodeGeneratorX86::VisitJdkUnsafeGetObject(HInvoke* invoke) {
1843 GenUnsafeGet(invoke, DataType::Type::kReference, /*is_volatile=*/ false, codegen_);
1844}
1845void IntrinsicCodeGeneratorX86::VisitJdkUnsafeGetObjectVolatile(HInvoke* invoke) {
1846 GenUnsafeGet(invoke, DataType::Type::kReference, /*is_volatile=*/ true, codegen_);
1847}
Sorin Basca507cf902021-10-06 12:04:56 +00001848void IntrinsicCodeGeneratorX86::VisitJdkUnsafeGetObjectAcquire(HInvoke* invoke) {
1849 GenUnsafeGet(invoke, DataType::Type::kReference, /*is_volatile=*/ true, codegen_);
1850}
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001851
Vladimir Markoca6fff82017-10-03 14:49:14 +01001852static void CreateIntIntIntIntToVoidPlusTempsLocations(ArenaAllocator* allocator,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001853 DataType::Type type,
Mark Mendell09ed1a32015-03-25 08:30:06 -04001854 HInvoke* invoke,
1855 bool is_volatile) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01001856 LocationSummary* locations =
1857 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001858 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
1859 locations->SetInAt(1, Location::RequiresRegister());
1860 locations->SetInAt(2, Location::RequiresRegister());
1861 locations->SetInAt(3, Location::RequiresRegister());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001862 if (type == DataType::Type::kReference) {
Mark Mendell09ed1a32015-03-25 08:30:06 -04001863 // Need temp registers for card-marking.
Roland Levillain4d027112015-07-01 15:41:14 +01001864 locations->AddTemp(Location::RequiresRegister()); // Possibly used for reference poisoning too.
Mark Mendell09ed1a32015-03-25 08:30:06 -04001865 // Ensure the value is in a byte register.
1866 locations->AddTemp(Location::RegisterLocation(ECX));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001867 } else if (type == DataType::Type::kInt64 && is_volatile) {
Mark Mendell09ed1a32015-03-25 08:30:06 -04001868 locations->AddTemp(Location::RequiresFpuRegister());
1869 locations->AddTemp(Location::RequiresFpuRegister());
1870 }
1871}
1872
1873void IntrinsicLocationsBuilderX86::VisitUnsafePut(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001874 VisitJdkUnsafePut(invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001875}
1876void IntrinsicLocationsBuilderX86::VisitUnsafePutOrdered(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001877 VisitJdkUnsafePutOrdered(invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001878}
1879void IntrinsicLocationsBuilderX86::VisitUnsafePutVolatile(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001880 VisitJdkUnsafePutVolatile(invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001881}
1882void IntrinsicLocationsBuilderX86::VisitUnsafePutObject(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001883 VisitJdkUnsafePutObject(invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001884}
1885void IntrinsicLocationsBuilderX86::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001886 VisitJdkUnsafePutObjectOrdered(invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001887}
1888void IntrinsicLocationsBuilderX86::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001889 VisitJdkUnsafePutObjectVolatile(invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001890}
1891void IntrinsicLocationsBuilderX86::VisitUnsafePutLong(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001892 VisitJdkUnsafePutLong(invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001893}
1894void IntrinsicLocationsBuilderX86::VisitUnsafePutLongOrdered(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001895 VisitJdkUnsafePutLongOrdered(invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001896}
1897void IntrinsicLocationsBuilderX86::VisitUnsafePutLongVolatile(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001898 VisitJdkUnsafePutLongVolatile(invoke);
1899}
1900
1901void IntrinsicLocationsBuilderX86::VisitJdkUnsafePut(HInvoke* invoke) {
Roland Levillainbf84a3d2015-12-04 14:33:02 +00001902 CreateIntIntIntIntToVoidPlusTempsLocations(
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001903 allocator_, DataType::Type::kInt32, invoke, /*is_volatile=*/ false);
1904}
1905void IntrinsicLocationsBuilderX86::VisitJdkUnsafePutOrdered(HInvoke* invoke) {
1906 CreateIntIntIntIntToVoidPlusTempsLocations(
1907 allocator_, DataType::Type::kInt32, invoke, /*is_volatile=*/ false);
1908}
1909void IntrinsicLocationsBuilderX86::VisitJdkUnsafePutVolatile(HInvoke* invoke) {
1910 CreateIntIntIntIntToVoidPlusTempsLocations(
1911 allocator_, DataType::Type::kInt32, invoke, /*is_volatile=*/ true);
1912}
Sorin Basca0069ad72021-09-17 17:33:09 +00001913void IntrinsicLocationsBuilderX86::VisitJdkUnsafePutRelease(HInvoke* invoke) {
1914 CreateIntIntIntIntToVoidPlusTempsLocations(
1915 allocator_, DataType::Type::kInt32, invoke, /*is_volatile=*/ true);
1916}
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001917void IntrinsicLocationsBuilderX86::VisitJdkUnsafePutObject(HInvoke* invoke) {
1918 CreateIntIntIntIntToVoidPlusTempsLocations(
1919 allocator_, DataType::Type::kReference, invoke, /*is_volatile=*/ false);
1920}
1921void IntrinsicLocationsBuilderX86::VisitJdkUnsafePutObjectOrdered(HInvoke* invoke) {
1922 CreateIntIntIntIntToVoidPlusTempsLocations(
1923 allocator_, DataType::Type::kReference, invoke, /*is_volatile=*/ false);
1924}
1925void IntrinsicLocationsBuilderX86::VisitJdkUnsafePutObjectVolatile(HInvoke* invoke) {
1926 CreateIntIntIntIntToVoidPlusTempsLocations(
1927 allocator_, DataType::Type::kReference, invoke, /*is_volatile=*/ true);
1928}
Sorin Basca507cf902021-10-06 12:04:56 +00001929void IntrinsicLocationsBuilderX86::VisitJdkUnsafePutObjectRelease(HInvoke* invoke) {
1930 CreateIntIntIntIntToVoidPlusTempsLocations(
1931 allocator_, DataType::Type::kReference, invoke, /*is_volatile=*/ true);
1932}
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001933void IntrinsicLocationsBuilderX86::VisitJdkUnsafePutLong(HInvoke* invoke) {
1934 CreateIntIntIntIntToVoidPlusTempsLocations(
1935 allocator_, DataType::Type::kInt64, invoke, /*is_volatile=*/ false);
1936}
1937void IntrinsicLocationsBuilderX86::VisitJdkUnsafePutLongOrdered(HInvoke* invoke) {
1938 CreateIntIntIntIntToVoidPlusTempsLocations(
1939 allocator_, DataType::Type::kInt64, invoke, /*is_volatile=*/ false);
1940}
1941void IntrinsicLocationsBuilderX86::VisitJdkUnsafePutLongVolatile(HInvoke* invoke) {
1942 CreateIntIntIntIntToVoidPlusTempsLocations(
1943 allocator_, DataType::Type::kInt64, invoke, /*is_volatile=*/ true);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001944}
Sorin Basca507cf902021-10-06 12:04:56 +00001945void IntrinsicLocationsBuilderX86::VisitJdkUnsafePutLongRelease(HInvoke* invoke) {
1946 CreateIntIntIntIntToVoidPlusTempsLocations(
1947 allocator_, DataType::Type::kInt64, invoke, /*is_volatile=*/ true);
1948}
Mark Mendell09ed1a32015-03-25 08:30:06 -04001949
1950// We don't care for ordered: it requires an AnyStore barrier, which is already given by the x86
1951// memory model.
1952static void GenUnsafePut(LocationSummary* locations,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001953 DataType::Type type,
Mark Mendell09ed1a32015-03-25 08:30:06 -04001954 bool is_volatile,
1955 CodeGeneratorX86* codegen) {
Roland Levillainb488b782015-10-22 11:38:49 +01001956 X86Assembler* assembler = down_cast<X86Assembler*>(codegen->GetAssembler());
Mark Mendell09ed1a32015-03-25 08:30:06 -04001957 Register base = locations->InAt(1).AsRegister<Register>();
1958 Register offset = locations->InAt(2).AsRegisterPairLow<Register>();
1959 Location value_loc = locations->InAt(3);
1960
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001961 if (type == DataType::Type::kInt64) {
Mark Mendell09ed1a32015-03-25 08:30:06 -04001962 Register value_lo = value_loc.AsRegisterPairLow<Register>();
1963 Register value_hi = value_loc.AsRegisterPairHigh<Register>();
1964 if (is_volatile) {
1965 XmmRegister temp1 = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
1966 XmmRegister temp2 = locations->GetTemp(1).AsFpuRegister<XmmRegister>();
1967 __ movd(temp1, value_lo);
1968 __ movd(temp2, value_hi);
1969 __ punpckldq(temp1, temp2);
1970 __ movsd(Address(base, offset, ScaleFactor::TIMES_1, 0), temp1);
1971 } else {
1972 __ movl(Address(base, offset, ScaleFactor::TIMES_1, 0), value_lo);
1973 __ movl(Address(base, offset, ScaleFactor::TIMES_1, 4), value_hi);
1974 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001975 } else if (kPoisonHeapReferences && type == DataType::Type::kReference) {
Roland Levillain4d027112015-07-01 15:41:14 +01001976 Register temp = locations->GetTemp(0).AsRegister<Register>();
1977 __ movl(temp, value_loc.AsRegister<Register>());
1978 __ PoisonHeapReference(temp);
1979 __ movl(Address(base, offset, ScaleFactor::TIMES_1, 0), temp);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001980 } else {
1981 __ movl(Address(base, offset, ScaleFactor::TIMES_1, 0), value_loc.AsRegister<Register>());
1982 }
1983
1984 if (is_volatile) {
Mark P Mendell17077d82015-12-16 19:15:59 +00001985 codegen->MemoryFence();
Mark Mendell09ed1a32015-03-25 08:30:06 -04001986 }
1987
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001988 if (type == DataType::Type::kReference) {
Nicolas Geoffray07276db2015-05-18 14:22:09 +01001989 bool value_can_be_null = true; // TODO: Worth finding out this information?
Mark Mendell09ed1a32015-03-25 08:30:06 -04001990 codegen->MarkGCCard(locations->GetTemp(0).AsRegister<Register>(),
1991 locations->GetTemp(1).AsRegister<Register>(),
1992 base,
Nicolas Geoffray07276db2015-05-18 14:22:09 +01001993 value_loc.AsRegister<Register>(),
1994 value_can_be_null);
Mark Mendell09ed1a32015-03-25 08:30:06 -04001995 }
1996}
1997
1998void IntrinsicCodeGeneratorX86::VisitUnsafePut(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00001999 VisitJdkUnsafePut(invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04002000}
2001void IntrinsicCodeGeneratorX86::VisitUnsafePutOrdered(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00002002 VisitJdkUnsafePutOrdered(invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04002003}
2004void IntrinsicCodeGeneratorX86::VisitUnsafePutVolatile(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00002005 VisitJdkUnsafePutVolatile(invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04002006}
2007void IntrinsicCodeGeneratorX86::VisitUnsafePutObject(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00002008 VisitJdkUnsafePutObject(invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04002009}
2010void IntrinsicCodeGeneratorX86::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00002011 VisitJdkUnsafePutObjectOrdered(invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04002012}
2013void IntrinsicCodeGeneratorX86::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00002014 VisitJdkUnsafePutObjectVolatile(invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04002015}
2016void IntrinsicCodeGeneratorX86::VisitUnsafePutLong(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00002017 VisitJdkUnsafePutLong(invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04002018}
2019void IntrinsicCodeGeneratorX86::VisitUnsafePutLongOrdered(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00002020 VisitJdkUnsafePutLongOrdered(invoke);
Mark Mendell09ed1a32015-03-25 08:30:06 -04002021}
2022void IntrinsicCodeGeneratorX86::VisitUnsafePutLongVolatile(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00002023 VisitJdkUnsafePutLongVolatile(invoke);
2024}
2025
2026void IntrinsicCodeGeneratorX86::VisitJdkUnsafePut(HInvoke* invoke) {
2027 GenUnsafePut(invoke->GetLocations(), DataType::Type::kInt32, /*is_volatile=*/ false, codegen_);
2028}
2029void IntrinsicCodeGeneratorX86::VisitJdkUnsafePutOrdered(HInvoke* invoke) {
2030 GenUnsafePut(invoke->GetLocations(), DataType::Type::kInt32, /*is_volatile=*/ false, codegen_);
2031}
2032void IntrinsicCodeGeneratorX86::VisitJdkUnsafePutVolatile(HInvoke* invoke) {
2033 GenUnsafePut(invoke->GetLocations(), DataType::Type::kInt32, /*is_volatile=*/ true, codegen_);
2034}
Sorin Basca0069ad72021-09-17 17:33:09 +00002035void IntrinsicCodeGeneratorX86::VisitJdkUnsafePutRelease(HInvoke* invoke) {
2036 GenUnsafePut(invoke->GetLocations(), DataType::Type::kInt32, /*is_volatile=*/ true, codegen_);
2037}
Sorin Basca2f01e8e2021-06-18 06:44:07 +00002038void IntrinsicCodeGeneratorX86::VisitJdkUnsafePutObject(HInvoke* invoke) {
2039 GenUnsafePut(
2040 invoke->GetLocations(), DataType::Type::kReference, /*is_volatile=*/ false, codegen_);
2041}
2042void IntrinsicCodeGeneratorX86::VisitJdkUnsafePutObjectOrdered(HInvoke* invoke) {
2043 GenUnsafePut(
2044 invoke->GetLocations(), DataType::Type::kReference, /*is_volatile=*/ false, codegen_);
2045}
2046void IntrinsicCodeGeneratorX86::VisitJdkUnsafePutObjectVolatile(HInvoke* invoke) {
2047 GenUnsafePut(
2048 invoke->GetLocations(), DataType::Type::kReference, /*is_volatile=*/ true, codegen_);
2049}
Sorin Basca507cf902021-10-06 12:04:56 +00002050void IntrinsicCodeGeneratorX86::VisitJdkUnsafePutObjectRelease(HInvoke* invoke) {
2051 GenUnsafePut(
2052 invoke->GetLocations(), DataType::Type::kReference, /*is_volatile=*/ true, codegen_);
2053}
Sorin Basca2f01e8e2021-06-18 06:44:07 +00002054void IntrinsicCodeGeneratorX86::VisitJdkUnsafePutLong(HInvoke* invoke) {
2055 GenUnsafePut(invoke->GetLocations(), DataType::Type::kInt64, /*is_volatile=*/ false, codegen_);
2056}
2057void IntrinsicCodeGeneratorX86::VisitJdkUnsafePutLongOrdered(HInvoke* invoke) {
2058 GenUnsafePut(invoke->GetLocations(), DataType::Type::kInt64, /*is_volatile=*/ false, codegen_);
2059}
2060void IntrinsicCodeGeneratorX86::VisitJdkUnsafePutLongVolatile(HInvoke* invoke) {
2061 GenUnsafePut(invoke->GetLocations(), DataType::Type::kInt64, /*is_volatile=*/ true, codegen_);
Mark Mendell09ed1a32015-03-25 08:30:06 -04002062}
Sorin Basca507cf902021-10-06 12:04:56 +00002063void IntrinsicCodeGeneratorX86::VisitJdkUnsafePutLongRelease(HInvoke* invoke) {
2064 GenUnsafePut(invoke->GetLocations(), DataType::Type::kInt64, /*is_volatile=*/ true, codegen_);
2065}
Mark Mendell09ed1a32015-03-25 08:30:06 -04002066
Vladimir Markoca6fff82017-10-03 14:49:14 +01002067static void CreateIntIntIntIntIntToInt(ArenaAllocator* allocator,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002068 DataType::Type type,
Mark Mendell58d25fd2015-04-03 14:52:31 -04002069 HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01002070 bool can_call = kEmitCompilerReadBarrier &&
2071 kUseBakerReadBarrier &&
Sorin Basca2f01e8e2021-06-18 06:44:07 +00002072 (invoke->GetIntrinsic() == Intrinsics::kUnsafeCASObject ||
2073 invoke->GetIntrinsic() == Intrinsics::kJdkUnsafeCASObject);
Vladimir Markoca6fff82017-10-03 14:49:14 +01002074 LocationSummary* locations =
2075 new (allocator) LocationSummary(invoke,
2076 can_call
2077 ? LocationSummary::kCallOnSlowPath
2078 : LocationSummary::kNoCall,
2079 kIntrinsified);
Mark Mendell58d25fd2015-04-03 14:52:31 -04002080 locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
2081 locations->SetInAt(1, Location::RequiresRegister());
2082 // Offset is a long, but in 32 bit mode, we only need the low word.
2083 // Can we update the invoke here to remove a TypeConvert to Long?
2084 locations->SetInAt(2, Location::RequiresRegister());
2085 // Expected value must be in EAX or EDX:EAX.
2086 // For long, new value must be in ECX:EBX.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002087 if (type == DataType::Type::kInt64) {
Mark Mendell58d25fd2015-04-03 14:52:31 -04002088 locations->SetInAt(3, Location::RegisterPairLocation(EAX, EDX));
2089 locations->SetInAt(4, Location::RegisterPairLocation(EBX, ECX));
2090 } else {
2091 locations->SetInAt(3, Location::RegisterLocation(EAX));
2092 locations->SetInAt(4, Location::RequiresRegister());
2093 }
2094
2095 // Force a byte register for the output.
2096 locations->SetOut(Location::RegisterLocation(EAX));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002097 if (type == DataType::Type::kReference) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01002098 // Need temporary registers for card-marking, and possibly for
2099 // (Baker) read barrier.
Roland Levillainb488b782015-10-22 11:38:49 +01002100 locations->AddTemp(Location::RequiresRegister()); // Possibly used for reference poisoning too.
Mark Mendell58d25fd2015-04-03 14:52:31 -04002101 // Need a byte register for marking.
2102 locations->AddTemp(Location::RegisterLocation(ECX));
2103 }
2104}
2105
2106void IntrinsicLocationsBuilderX86::VisitUnsafeCASInt(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00002107 VisitJdkUnsafeCASInt(invoke);
Mark Mendell58d25fd2015-04-03 14:52:31 -04002108}
2109
2110void IntrinsicLocationsBuilderX86::VisitUnsafeCASLong(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00002111 VisitJdkUnsafeCASLong(invoke);
Mark Mendell58d25fd2015-04-03 14:52:31 -04002112}
2113
2114void IntrinsicLocationsBuilderX86::VisitUnsafeCASObject(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00002115 VisitJdkUnsafeCASObject(invoke);
2116}
2117
2118void IntrinsicLocationsBuilderX86::VisitJdkUnsafeCASInt(HInvoke* invoke) {
2119 CreateIntIntIntIntIntToInt(allocator_, DataType::Type::kInt32, invoke);
2120}
2121
2122void IntrinsicLocationsBuilderX86::VisitJdkUnsafeCASLong(HInvoke* invoke) {
2123 CreateIntIntIntIntIntToInt(allocator_, DataType::Type::kInt64, invoke);
2124}
2125
2126void IntrinsicLocationsBuilderX86::VisitJdkUnsafeCASObject(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01002127 // The only read barrier implementation supporting the
Sorin Basca2f01e8e2021-06-18 06:44:07 +00002128 // JdkUnsafeCASObject intrinsic is the Baker-style read barriers.
Roland Levillaina1aa3b12016-10-26 13:03:38 +01002129 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
Roland Levillain391b8662015-12-18 11:43:38 +00002130 return;
2131 }
2132
Vladimir Markoca6fff82017-10-03 14:49:14 +01002133 CreateIntIntIntIntIntToInt(allocator_, DataType::Type::kReference, invoke);
Mark Mendell58d25fd2015-04-03 14:52:31 -04002134}
2135
Sorin Basca0069ad72021-09-17 17:33:09 +00002136void IntrinsicLocationsBuilderX86::VisitJdkUnsafeCompareAndSetInt(HInvoke* invoke) {
2137 CreateIntIntIntIntIntToInt(allocator_, DataType::Type::kInt32, invoke);
2138}
2139
Sorin Basca507cf902021-10-06 12:04:56 +00002140void IntrinsicLocationsBuilderX86::VisitJdkUnsafeCompareAndSetLong(HInvoke* invoke) {
2141 CreateIntIntIntIntIntToInt(allocator_, DataType::Type::kInt64, invoke);
2142}
2143
Andra Danciueb2c9dd2020-09-14 13:22:40 +00002144static void GenPrimitiveLockedCmpxchg(DataType::Type type,
2145 CodeGeneratorX86* codegen,
2146 Location expected_value,
2147 Location new_value,
2148 Register base,
2149 Register offset,
2150 // Only necessary for floating point
2151 Register temp = Register::kNoRegister) {
Roland Levillainb488b782015-10-22 11:38:49 +01002152 X86Assembler* assembler = down_cast<X86Assembler*>(codegen->GetAssembler());
Andra Danciu8d8380a2020-09-11 09:24:01 +00002153
Andra Danciu8d8380a2020-09-11 09:24:01 +00002154 if (DataType::Kind(type) == DataType::Type::kInt32) {
2155 DCHECK_EQ(expected_value.AsRegister<Register>(), EAX);
2156 }
2157
2158 // The address of the field within the holding object.
2159 Address field_addr(base, offset, TIMES_1, 0);
2160
2161 switch (type) {
2162 case DataType::Type::kBool:
2163 case DataType::Type::kInt8:
2164 __ LockCmpxchgb(field_addr, new_value.AsRegister<ByteRegister>());
2165 break;
2166 case DataType::Type::kInt16:
2167 case DataType::Type::kUint16:
2168 __ LockCmpxchgw(field_addr, new_value.AsRegister<Register>());
2169 break;
2170 case DataType::Type::kInt32:
2171 __ LockCmpxchgl(field_addr, new_value.AsRegister<Register>());
2172 break;
2173 case DataType::Type::kFloat32: {
2174 // cmpxchg requires the expected value to be in EAX so the new value must be elsewhere.
2175 DCHECK_NE(temp, EAX);
2176 // EAX is both an input and an output for cmpxchg
2177 codegen->Move32(Location::RegisterLocation(EAX), expected_value);
2178 codegen->Move32(Location::RegisterLocation(temp), new_value);
2179 __ LockCmpxchgl(field_addr, temp);
2180 break;
2181 }
2182 case DataType::Type::kInt64:
2183 // Ensure the expected value is in EAX:EDX and that the new
2184 // value is in EBX:ECX (required by the CMPXCHG8B instruction).
2185 DCHECK_EQ(expected_value.AsRegisterPairLow<Register>(), EAX);
2186 DCHECK_EQ(expected_value.AsRegisterPairHigh<Register>(), EDX);
2187 DCHECK_EQ(new_value.AsRegisterPairLow<Register>(), EBX);
2188 DCHECK_EQ(new_value.AsRegisterPairHigh<Register>(), ECX);
2189 __ LockCmpxchg8b(field_addr);
2190 break;
2191 default:
2192 LOG(FATAL) << "Unexpected CAS type " << type;
2193 }
2194 // LOCK CMPXCHG/LOCK CMPXCHG8B have full barrier semantics, and we
2195 // don't need scheduling barriers at this time.
Andra Danciueb2c9dd2020-09-14 13:22:40 +00002196}
2197
2198static void GenPrimitiveCAS(DataType::Type type,
2199 CodeGeneratorX86* codegen,
2200 Location expected_value,
2201 Location new_value,
2202 Register base,
2203 Register offset,
2204 Location out,
2205 // Only necessary for floating point
Andra Danciu370948e2020-09-23 08:07:25 +00002206 Register temp = Register::kNoRegister,
2207 bool is_cmpxchg = false) {
Andra Danciueb2c9dd2020-09-14 13:22:40 +00002208 X86Assembler* assembler = down_cast<X86Assembler*>(codegen->GetAssembler());
Andra Danciu370948e2020-09-23 08:07:25 +00002209
2210 if (!is_cmpxchg || DataType::Kind(type) == DataType::Type::kInt32) {
2211 DCHECK_EQ(out.AsRegister<Register>(), EAX);
2212 }
Andra Danciueb2c9dd2020-09-14 13:22:40 +00002213
2214 GenPrimitiveLockedCmpxchg(type, codegen, expected_value, new_value, base, offset, temp);
Andra Danciu8d8380a2020-09-11 09:24:01 +00002215
Andra Danciu370948e2020-09-23 08:07:25 +00002216 if (is_cmpxchg) {
2217 // Sign-extend, zero-extend or move the result if necessary
2218 switch (type) {
2219 case DataType::Type::kBool:
2220 __ movzxb(out.AsRegister<Register>(), out.AsRegister<ByteRegister>());
2221 break;
2222 case DataType::Type::kInt8:
2223 __ movsxb(out.AsRegister<Register>(), out.AsRegister<ByteRegister>());
2224 break;
2225 case DataType::Type::kInt16:
2226 __ movsxw(out.AsRegister<Register>(), out.AsRegister<Register>());
2227 break;
2228 case DataType::Type::kUint16:
2229 __ movzxw(out.AsRegister<Register>(), out.AsRegister<Register>());
2230 break;
2231 case DataType::Type::kFloat32:
2232 __ movd(out.AsFpuRegister<XmmRegister>(), EAX);
2233 break;
2234 default:
2235 // Nothing to do
2236 break;
2237 }
2238 } else {
2239 // Convert ZF into the Boolean result.
2240 __ setb(kZero, out.AsRegister<Register>());
2241 __ movzxb(out.AsRegister<Register>(), out.AsRegister<ByteRegister>());
2242 }
Andra Danciu8d8380a2020-09-11 09:24:01 +00002243}
2244
2245static void GenReferenceCAS(HInvoke* invoke,
2246 CodeGeneratorX86* codegen,
2247 Location expected_value,
2248 Location new_value,
2249 Register base,
2250 Register offset,
2251 Register temp,
Andra Danciu370948e2020-09-23 08:07:25 +00002252 Register temp2,
2253 bool is_cmpxchg = false) {
Andra Danciu8d8380a2020-09-11 09:24:01 +00002254 X86Assembler* assembler = down_cast<X86Assembler*>(codegen->GetAssembler());
2255 LocationSummary* locations = invoke->GetLocations();
2256 Location out = locations->Out();
2257
2258 // The address of the field within the holding object.
2259 Address field_addr(base, offset, TIMES_1, 0);
2260
Andra Danciuafad9f92020-09-15 15:38:32 +00002261 Register value = new_value.AsRegister<Register>();
Andra Danciu8d8380a2020-09-11 09:24:01 +00002262 Register expected = expected_value.AsRegister<Register>();
2263 DCHECK_EQ(expected, EAX);
2264 DCHECK_NE(temp, temp2);
2265
Andra Danciu8d8380a2020-09-11 09:24:01 +00002266 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2267 // Need to make sure the reference stored in the field is a to-space
2268 // one before attempting the CAS or the CAS could fail incorrectly.
2269 codegen->GenerateReferenceLoadWithBakerReadBarrier(
2270 invoke,
2271 // Unused, used only as a "temporary" within the read barrier.
2272 Location::RegisterLocation(temp),
2273 base,
2274 field_addr,
2275 /* needs_null_check= */ false,
2276 /* always_update_field= */ true,
2277 &temp2);
2278 }
2279 bool base_equals_value = (base == value);
2280 if (kPoisonHeapReferences) {
2281 if (base_equals_value) {
2282 // If `base` and `value` are the same register location, move
2283 // `value` to a temporary register. This way, poisoning
2284 // `value` won't invalidate `base`.
2285 value = temp;
2286 __ movl(value, base);
2287 }
2288
2289 // Check that the register allocator did not assign the location
2290 // of `expected` (EAX) to `value` nor to `base`, so that heap
2291 // poisoning (when enabled) works as intended below.
2292 // - If `value` were equal to `expected`, both references would
2293 // be poisoned twice, meaning they would not be poisoned at
2294 // all, as heap poisoning uses address negation.
2295 // - If `base` were equal to `expected`, poisoning `expected`
2296 // would invalidate `base`.
2297 DCHECK_NE(value, expected);
2298 DCHECK_NE(base, expected);
2299 __ PoisonHeapReference(expected);
2300 __ PoisonHeapReference(value);
2301 }
2302 __ LockCmpxchgl(field_addr, value);
2303
2304 // LOCK CMPXCHG has full barrier semantics, and we don't need
2305 // scheduling barriers at this time.
2306
Andra Danciu370948e2020-09-23 08:07:25 +00002307 if (is_cmpxchg) {
2308 DCHECK_EQ(out.AsRegister<Register>(), EAX);
2309 __ MaybeUnpoisonHeapReference(out.AsRegister<Register>());
2310 } else {
2311 // Convert ZF into the Boolean result.
2312 __ setb(kZero, out.AsRegister<Register>());
2313 __ movzxb(out.AsRegister<Register>(), out.AsRegister<ByteRegister>());
2314 }
Andra Danciu8d8380a2020-09-11 09:24:01 +00002315
Andra Danciuafad9f92020-09-15 15:38:32 +00002316 // Mark card for object if the new value is stored.
2317 bool value_can_be_null = true; // TODO: Worth finding out this information?
2318 NearLabel skip_mark_gc_card;
2319 __ j(kNotZero, &skip_mark_gc_card);
2320 codegen->MarkGCCard(temp, temp2, base, value, value_can_be_null);
2321 __ Bind(&skip_mark_gc_card);
2322
Andra Danciu8d8380a2020-09-11 09:24:01 +00002323 // If heap poisoning is enabled, we need to unpoison the values
2324 // that were poisoned earlier.
2325 if (kPoisonHeapReferences) {
2326 if (base_equals_value) {
2327 // `value` has been moved to a temporary register, no need to
2328 // unpoison it.
2329 } else {
2330 // Ensure `value` is different from `out`, so that unpoisoning
2331 // the former does not invalidate the latter.
2332 DCHECK_NE(value, out.AsRegister<Register>());
2333 __ UnpoisonHeapReference(value);
2334 }
2335 }
2336 // Do not unpoison the reference contained in register
2337 // `expected`, as it is the same as register `out` (EAX).
2338}
2339
2340static void GenCAS(DataType::Type type, HInvoke* invoke, CodeGeneratorX86* codegen) {
Mark Mendell58d25fd2015-04-03 14:52:31 -04002341 LocationSummary* locations = invoke->GetLocations();
2342
2343 Register base = locations->InAt(1).AsRegister<Register>();
2344 Register offset = locations->InAt(2).AsRegisterPairLow<Register>();
Andra Danciu8d8380a2020-09-11 09:24:01 +00002345 Location expected_value = locations->InAt(3);
2346 Location new_value = locations->InAt(4);
Mark Mendell58d25fd2015-04-03 14:52:31 -04002347 Location out = locations->Out();
2348 DCHECK_EQ(out.AsRegister<Register>(), EAX);
2349
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002350 if (type == DataType::Type::kReference) {
Andra Danciuff6d5fa2020-09-16 07:08:24 +00002351 // The only read barrier implementation supporting the
2352 // UnsafeCASObject intrinsic is the Baker-style read barriers.
2353 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
2354
Andra Danciu8d8380a2020-09-11 09:24:01 +00002355 Register temp = locations->GetTemp(0).AsRegister<Register>();
Roland Levillaina1aa3b12016-10-26 13:03:38 +01002356 Register temp2 = locations->GetTemp(1).AsRegister<Register>();
Andra Danciu8d8380a2020-09-11 09:24:01 +00002357 GenReferenceCAS(invoke, codegen, expected_value, new_value, base, offset, temp, temp2);
Roland Levillainb488b782015-10-22 11:38:49 +01002358 } else {
Andra Danciu8d8380a2020-09-11 09:24:01 +00002359 DCHECK(!DataType::IsFloatingPointType(type));
2360 GenPrimitiveCAS(type, codegen, expected_value, new_value, base, offset, out);
Roland Levillain4d027112015-07-01 15:41:14 +01002361 }
Mark Mendell58d25fd2015-04-03 14:52:31 -04002362}
2363
2364void IntrinsicCodeGeneratorX86::VisitUnsafeCASInt(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00002365 VisitJdkUnsafeCASInt(invoke);
Mark Mendell58d25fd2015-04-03 14:52:31 -04002366}
2367
2368void IntrinsicCodeGeneratorX86::VisitUnsafeCASLong(HInvoke* invoke) {
Sorin Basca2f01e8e2021-06-18 06:44:07 +00002369 VisitJdkUnsafeCASLong(invoke);
Mark Mendell58d25fd2015-04-03 14:52:31 -04002370}
2371
2372void IntrinsicCodeGeneratorX86::VisitUnsafeCASObject(HInvoke* invoke) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01002373 // The only read barrier implementation supporting the
2374 // UnsafeCASObject intrinsic is the Baker-style read barriers.
2375 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
Roland Levillain3d312422016-06-23 13:53:42 +01002376
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002377 GenCAS(DataType::Type::kReference, invoke, codegen_);
Mark Mendell58d25fd2015-04-03 14:52:31 -04002378}
2379
Sorin Basca2f01e8e2021-06-18 06:44:07 +00002380void IntrinsicCodeGeneratorX86::VisitJdkUnsafeCASInt(HInvoke* invoke) {
2381 GenCAS(DataType::Type::kInt32, invoke, codegen_);
2382}
2383
2384void IntrinsicCodeGeneratorX86::VisitJdkUnsafeCASLong(HInvoke* invoke) {
2385 GenCAS(DataType::Type::kInt64, invoke, codegen_);
2386}
2387
2388void IntrinsicCodeGeneratorX86::VisitJdkUnsafeCASObject(HInvoke* invoke) {
2389 // The only read barrier implementation supporting the
2390 // JdkUnsafeCASObject intrinsic is the Baker-style read barriers.
2391 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
2392
2393 GenCAS(DataType::Type::kReference, invoke, codegen_);
2394}
2395
Sorin Basca0069ad72021-09-17 17:33:09 +00002396void IntrinsicCodeGeneratorX86::VisitJdkUnsafeCompareAndSetInt(HInvoke* invoke) {
2397 GenCAS(DataType::Type::kInt32, invoke, codegen_);
2398}
2399
Sorin Basca507cf902021-10-06 12:04:56 +00002400void IntrinsicCodeGeneratorX86::VisitJdkUnsafeCompareAndSetLong(HInvoke* invoke) {
2401 GenCAS(DataType::Type::kInt64, invoke, codegen_);
2402}
2403
Sorin Basca2f01e8e2021-06-18 06:44:07 +00002404
Mark Mendell58d25fd2015-04-03 14:52:31 -04002405void IntrinsicLocationsBuilderX86::VisitIntegerReverse(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002406 LocationSummary* locations =
2407 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Mark Mendell58d25fd2015-04-03 14:52:31 -04002408 locations->SetInAt(0, Location::RequiresRegister());
2409 locations->SetOut(Location::SameAsFirstInput());
2410 locations->AddTemp(Location::RequiresRegister());
2411}
2412
2413static void SwapBits(Register reg, Register temp, int32_t shift, int32_t mask,
2414 X86Assembler* assembler) {
2415 Immediate imm_shift(shift);
2416 Immediate imm_mask(mask);
2417 __ movl(temp, reg);
2418 __ shrl(reg, imm_shift);
2419 __ andl(temp, imm_mask);
2420 __ andl(reg, imm_mask);
2421 __ shll(temp, imm_shift);
2422 __ orl(reg, temp);
2423}
2424
2425void IntrinsicCodeGeneratorX86::VisitIntegerReverse(HInvoke* invoke) {
Aart Bika19616e2016-02-01 18:57:58 -08002426 X86Assembler* assembler = GetAssembler();
Mark Mendell58d25fd2015-04-03 14:52:31 -04002427 LocationSummary* locations = invoke->GetLocations();
2428
2429 Register reg = locations->InAt(0).AsRegister<Register>();
2430 Register temp = locations->GetTemp(0).AsRegister<Register>();
2431
2432 /*
2433 * Use one bswap instruction to reverse byte order first and then use 3 rounds of
2434 * swapping bits to reverse bits in a number x. Using bswap to save instructions
2435 * compared to generic luni implementation which has 5 rounds of swapping bits.
2436 * x = bswap x
2437 * x = (x & 0x55555555) << 1 | (x >> 1) & 0x55555555;
2438 * x = (x & 0x33333333) << 2 | (x >> 2) & 0x33333333;
2439 * x = (x & 0x0F0F0F0F) << 4 | (x >> 4) & 0x0F0F0F0F;
2440 */
2441 __ bswapl(reg);
2442 SwapBits(reg, temp, 1, 0x55555555, assembler);
2443 SwapBits(reg, temp, 2, 0x33333333, assembler);
2444 SwapBits(reg, temp, 4, 0x0f0f0f0f, assembler);
2445}
2446
2447void IntrinsicLocationsBuilderX86::VisitLongReverse(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002448 LocationSummary* locations =
2449 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Mark Mendell58d25fd2015-04-03 14:52:31 -04002450 locations->SetInAt(0, Location::RequiresRegister());
2451 locations->SetOut(Location::SameAsFirstInput());
2452 locations->AddTemp(Location::RequiresRegister());
2453}
2454
2455void IntrinsicCodeGeneratorX86::VisitLongReverse(HInvoke* invoke) {
Aart Bika19616e2016-02-01 18:57:58 -08002456 X86Assembler* assembler = GetAssembler();
Mark Mendell58d25fd2015-04-03 14:52:31 -04002457 LocationSummary* locations = invoke->GetLocations();
2458
2459 Register reg_low = locations->InAt(0).AsRegisterPairLow<Register>();
2460 Register reg_high = locations->InAt(0).AsRegisterPairHigh<Register>();
2461 Register temp = locations->GetTemp(0).AsRegister<Register>();
2462
2463 // We want to swap high/low, then bswap each one, and then do the same
2464 // as a 32 bit reverse.
2465 // Exchange high and low.
2466 __ movl(temp, reg_low);
2467 __ movl(reg_low, reg_high);
2468 __ movl(reg_high, temp);
2469
2470 // bit-reverse low
2471 __ bswapl(reg_low);
2472 SwapBits(reg_low, temp, 1, 0x55555555, assembler);
2473 SwapBits(reg_low, temp, 2, 0x33333333, assembler);
2474 SwapBits(reg_low, temp, 4, 0x0f0f0f0f, assembler);
2475
2476 // bit-reverse high
2477 __ bswapl(reg_high);
2478 SwapBits(reg_high, temp, 1, 0x55555555, assembler);
2479 SwapBits(reg_high, temp, 2, 0x33333333, assembler);
2480 SwapBits(reg_high, temp, 4, 0x0f0f0f0f, assembler);
2481}
2482
Aart Bikc39dac12016-01-21 08:59:48 -08002483static void CreateBitCountLocations(
Vladimir Markoca6fff82017-10-03 14:49:14 +01002484 ArenaAllocator* allocator, CodeGeneratorX86* codegen, HInvoke* invoke, bool is_long) {
Aart Bikc39dac12016-01-21 08:59:48 -08002485 if (!codegen->GetInstructionSetFeatures().HasPopCnt()) {
2486 // Do nothing if there is no popcnt support. This results in generating
2487 // a call for the intrinsic rather than direct code.
2488 return;
2489 }
Vladimir Markoca6fff82017-10-03 14:49:14 +01002490 LocationSummary* locations =
2491 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Aart Bikc39dac12016-01-21 08:59:48 -08002492 if (is_long) {
Aart Bikc39dac12016-01-21 08:59:48 -08002493 locations->AddTemp(Location::RequiresRegister());
Aart Bikc39dac12016-01-21 08:59:48 -08002494 }
Aart Bik2a946072016-01-21 12:49:00 -08002495 locations->SetInAt(0, Location::Any());
Aart Bikc39dac12016-01-21 08:59:48 -08002496 locations->SetOut(Location::RequiresRegister());
2497}
2498
Aart Bika19616e2016-02-01 18:57:58 -08002499static void GenBitCount(X86Assembler* assembler,
2500 CodeGeneratorX86* codegen,
2501 HInvoke* invoke, bool is_long) {
Aart Bikc39dac12016-01-21 08:59:48 -08002502 LocationSummary* locations = invoke->GetLocations();
2503 Location src = locations->InAt(0);
2504 Register out = locations->Out().AsRegister<Register>();
2505
2506 if (invoke->InputAt(0)->IsConstant()) {
2507 // Evaluate this at compile time.
2508 int64_t value = Int64FromConstant(invoke->InputAt(0)->AsConstant());
Roland Levillainfa3912e2016-04-01 18:21:55 +01002509 int32_t result = is_long
Aart Bikc39dac12016-01-21 08:59:48 -08002510 ? POPCOUNT(static_cast<uint64_t>(value))
2511 : POPCOUNT(static_cast<uint32_t>(value));
Roland Levillainfa3912e2016-04-01 18:21:55 +01002512 codegen->Load32BitValue(out, result);
Aart Bikc39dac12016-01-21 08:59:48 -08002513 return;
2514 }
2515
2516 // Handle the non-constant cases.
2517 if (!is_long) {
2518 if (src.IsRegister()) {
2519 __ popcntl(out, src.AsRegister<Register>());
2520 } else {
2521 DCHECK(src.IsStackSlot());
2522 __ popcntl(out, Address(ESP, src.GetStackIndex()));
2523 }
Aart Bik2a946072016-01-21 12:49:00 -08002524 } else {
2525 // The 64-bit case needs to worry about two parts.
2526 Register temp = locations->GetTemp(0).AsRegister<Register>();
2527 if (src.IsRegisterPair()) {
2528 __ popcntl(temp, src.AsRegisterPairLow<Register>());
2529 __ popcntl(out, src.AsRegisterPairHigh<Register>());
2530 } else {
2531 DCHECK(src.IsDoubleStackSlot());
2532 __ popcntl(temp, Address(ESP, src.GetStackIndex()));
2533 __ popcntl(out, Address(ESP, src.GetHighStackIndex(kX86WordSize)));
2534 }
2535 __ addl(out, temp);
Aart Bikc39dac12016-01-21 08:59:48 -08002536 }
Aart Bikc39dac12016-01-21 08:59:48 -08002537}
2538
2539void IntrinsicLocationsBuilderX86::VisitIntegerBitCount(HInvoke* invoke) {
Andreas Gampe3db70682018-12-26 15:12:03 -08002540 CreateBitCountLocations(allocator_, codegen_, invoke, /* is_long= */ false);
Aart Bikc39dac12016-01-21 08:59:48 -08002541}
2542
2543void IntrinsicCodeGeneratorX86::VisitIntegerBitCount(HInvoke* invoke) {
Andreas Gampe3db70682018-12-26 15:12:03 -08002544 GenBitCount(GetAssembler(), codegen_, invoke, /* is_long= */ false);
Aart Bikc39dac12016-01-21 08:59:48 -08002545}
2546
2547void IntrinsicLocationsBuilderX86::VisitLongBitCount(HInvoke* invoke) {
Andreas Gampe3db70682018-12-26 15:12:03 -08002548 CreateBitCountLocations(allocator_, codegen_, invoke, /* is_long= */ true);
Aart Bikc39dac12016-01-21 08:59:48 -08002549}
2550
2551void IntrinsicCodeGeneratorX86::VisitLongBitCount(HInvoke* invoke) {
Andreas Gampe3db70682018-12-26 15:12:03 -08002552 GenBitCount(GetAssembler(), codegen_, invoke, /* is_long= */ true);
Aart Bikc39dac12016-01-21 08:59:48 -08002553}
2554
Vladimir Markoca6fff82017-10-03 14:49:14 +01002555static void CreateLeadingZeroLocations(ArenaAllocator* allocator, HInvoke* invoke, bool is_long) {
2556 LocationSummary* locations =
2557 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Mark Mendelld5897672015-08-12 21:16:41 -04002558 if (is_long) {
2559 locations->SetInAt(0, Location::RequiresRegister());
2560 } else {
2561 locations->SetInAt(0, Location::Any());
2562 }
2563 locations->SetOut(Location::RequiresRegister());
2564}
2565
Aart Bika19616e2016-02-01 18:57:58 -08002566static void GenLeadingZeros(X86Assembler* assembler,
2567 CodeGeneratorX86* codegen,
2568 HInvoke* invoke, bool is_long) {
Mark Mendelld5897672015-08-12 21:16:41 -04002569 LocationSummary* locations = invoke->GetLocations();
2570 Location src = locations->InAt(0);
2571 Register out = locations->Out().AsRegister<Register>();
2572
2573 if (invoke->InputAt(0)->IsConstant()) {
2574 // Evaluate this at compile time.
2575 int64_t value = Int64FromConstant(invoke->InputAt(0)->AsConstant());
2576 if (value == 0) {
2577 value = is_long ? 64 : 32;
2578 } else {
2579 value = is_long ? CLZ(static_cast<uint64_t>(value)) : CLZ(static_cast<uint32_t>(value));
2580 }
Aart Bika19616e2016-02-01 18:57:58 -08002581 codegen->Load32BitValue(out, value);
Mark Mendelld5897672015-08-12 21:16:41 -04002582 return;
2583 }
2584
2585 // Handle the non-constant cases.
2586 if (!is_long) {
2587 if (src.IsRegister()) {
2588 __ bsrl(out, src.AsRegister<Register>());
2589 } else {
2590 DCHECK(src.IsStackSlot());
2591 __ bsrl(out, Address(ESP, src.GetStackIndex()));
2592 }
2593
2594 // BSR sets ZF if the input was zero, and the output is undefined.
Mark Mendell0c9497d2015-08-21 09:30:05 -04002595 NearLabel all_zeroes, done;
Mark Mendelld5897672015-08-12 21:16:41 -04002596 __ j(kEqual, &all_zeroes);
2597
2598 // Correct the result from BSR to get the final CLZ result.
2599 __ xorl(out, Immediate(31));
2600 __ jmp(&done);
2601
2602 // Fix the zero case with the expected result.
2603 __ Bind(&all_zeroes);
2604 __ movl(out, Immediate(32));
2605
2606 __ Bind(&done);
2607 return;
2608 }
2609
2610 // 64 bit case needs to worry about both parts of the register.
2611 DCHECK(src.IsRegisterPair());
2612 Register src_lo = src.AsRegisterPairLow<Register>();
2613 Register src_hi = src.AsRegisterPairHigh<Register>();
Mark Mendell0c9497d2015-08-21 09:30:05 -04002614 NearLabel handle_low, done, all_zeroes;
Mark Mendelld5897672015-08-12 21:16:41 -04002615
2616 // Is the high word zero?
2617 __ testl(src_hi, src_hi);
2618 __ j(kEqual, &handle_low);
2619
2620 // High word is not zero. We know that the BSR result is defined in this case.
2621 __ bsrl(out, src_hi);
2622
2623 // Correct the result from BSR to get the final CLZ result.
2624 __ xorl(out, Immediate(31));
2625 __ jmp(&done);
2626
2627 // High word was zero. We have to compute the low word count and add 32.
2628 __ Bind(&handle_low);
2629 __ bsrl(out, src_lo);
2630 __ j(kEqual, &all_zeroes);
2631
2632 // We had a valid result. Use an XOR to both correct the result and add 32.
2633 __ xorl(out, Immediate(63));
2634 __ jmp(&done);
2635
2636 // All zero case.
2637 __ Bind(&all_zeroes);
2638 __ movl(out, Immediate(64));
2639
2640 __ Bind(&done);
2641}
2642
2643void IntrinsicLocationsBuilderX86::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
Andreas Gampe3db70682018-12-26 15:12:03 -08002644 CreateLeadingZeroLocations(allocator_, invoke, /* is_long= */ false);
Mark Mendelld5897672015-08-12 21:16:41 -04002645}
2646
2647void IntrinsicCodeGeneratorX86::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
Andreas Gampe3db70682018-12-26 15:12:03 -08002648 GenLeadingZeros(GetAssembler(), codegen_, invoke, /* is_long= */ false);
Mark Mendelld5897672015-08-12 21:16:41 -04002649}
2650
2651void IntrinsicLocationsBuilderX86::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
Andreas Gampe3db70682018-12-26 15:12:03 -08002652 CreateLeadingZeroLocations(allocator_, invoke, /* is_long= */ true);
Mark Mendelld5897672015-08-12 21:16:41 -04002653}
2654
2655void IntrinsicCodeGeneratorX86::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
Andreas Gampe3db70682018-12-26 15:12:03 -08002656 GenLeadingZeros(GetAssembler(), codegen_, invoke, /* is_long= */ true);
Mark Mendelld5897672015-08-12 21:16:41 -04002657}
2658
Vladimir Markoca6fff82017-10-03 14:49:14 +01002659static void CreateTrailingZeroLocations(ArenaAllocator* allocator, HInvoke* invoke, bool is_long) {
2660 LocationSummary* locations =
2661 new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Mark Mendell2d554792015-09-15 21:45:18 -04002662 if (is_long) {
2663 locations->SetInAt(0, Location::RequiresRegister());
2664 } else {
2665 locations->SetInAt(0, Location::Any());
2666 }
2667 locations->SetOut(Location::RequiresRegister());
2668}
2669
Aart Bika19616e2016-02-01 18:57:58 -08002670static void GenTrailingZeros(X86Assembler* assembler,
2671 CodeGeneratorX86* codegen,
2672 HInvoke* invoke, bool is_long) {
Mark Mendell2d554792015-09-15 21:45:18 -04002673 LocationSummary* locations = invoke->GetLocations();
2674 Location src = locations->InAt(0);
2675 Register out = locations->Out().AsRegister<Register>();
2676
2677 if (invoke->InputAt(0)->IsConstant()) {
2678 // Evaluate this at compile time.
2679 int64_t value = Int64FromConstant(invoke->InputAt(0)->AsConstant());
2680 if (value == 0) {
2681 value = is_long ? 64 : 32;
2682 } else {
2683 value = is_long ? CTZ(static_cast<uint64_t>(value)) : CTZ(static_cast<uint32_t>(value));
2684 }
Aart Bika19616e2016-02-01 18:57:58 -08002685 codegen->Load32BitValue(out, value);
Mark Mendell2d554792015-09-15 21:45:18 -04002686 return;
2687 }
2688
2689 // Handle the non-constant cases.
2690 if (!is_long) {
2691 if (src.IsRegister()) {
2692 __ bsfl(out, src.AsRegister<Register>());
2693 } else {
2694 DCHECK(src.IsStackSlot());
2695 __ bsfl(out, Address(ESP, src.GetStackIndex()));
2696 }
2697
2698 // BSF sets ZF if the input was zero, and the output is undefined.
2699 NearLabel done;
2700 __ j(kNotEqual, &done);
2701
2702 // Fix the zero case with the expected result.
2703 __ movl(out, Immediate(32));
2704
2705 __ Bind(&done);
2706 return;
2707 }
2708
2709 // 64 bit case needs to worry about both parts of the register.
2710 DCHECK(src.IsRegisterPair());
2711 Register src_lo = src.AsRegisterPairLow<Register>();
2712 Register src_hi = src.AsRegisterPairHigh<Register>();
2713 NearLabel done, all_zeroes;
2714
2715 // If the low word is zero, then ZF will be set. If not, we have the answer.
2716 __ bsfl(out, src_lo);
2717 __ j(kNotEqual, &done);
2718
2719 // Low word was zero. We have to compute the high word count and add 32.
2720 __ bsfl(out, src_hi);
2721 __ j(kEqual, &all_zeroes);
2722
2723 // We had a valid result. Add 32 to account for the low word being zero.
2724 __ addl(out, Immediate(32));
2725 __ jmp(&done);
2726
2727 // All zero case.
2728 __ Bind(&all_zeroes);
2729 __ movl(out, Immediate(64));
2730
2731 __ Bind(&done);
2732}
2733
2734void IntrinsicLocationsBuilderX86::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
Andreas Gampe3db70682018-12-26 15:12:03 -08002735 CreateTrailingZeroLocations(allocator_, invoke, /* is_long= */ false);
Mark Mendell2d554792015-09-15 21:45:18 -04002736}
2737
2738void IntrinsicCodeGeneratorX86::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
Andreas Gampe3db70682018-12-26 15:12:03 -08002739 GenTrailingZeros(GetAssembler(), codegen_, invoke, /* is_long= */ false);
Mark Mendell2d554792015-09-15 21:45:18 -04002740}
2741
2742void IntrinsicLocationsBuilderX86::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
Andreas Gampe3db70682018-12-26 15:12:03 -08002743 CreateTrailingZeroLocations(allocator_, invoke, /* is_long= */ true);
Mark Mendell2d554792015-09-15 21:45:18 -04002744}
2745
2746void IntrinsicCodeGeneratorX86::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
Andreas Gampe3db70682018-12-26 15:12:03 -08002747 GenTrailingZeros(GetAssembler(), codegen_, invoke, /* is_long= */ true);
Mark Mendell2d554792015-09-15 21:45:18 -04002748}
2749
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002750static bool IsSameInput(HInstruction* instruction, size_t input0, size_t input1) {
2751 return instruction->InputAt(input0) == instruction->InputAt(input1);
2752}
2753
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002754// Compute base address for the System.arraycopy intrinsic in `base`.
2755static void GenSystemArrayCopyBaseAddress(X86Assembler* assembler,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002756 DataType::Type type,
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002757 const Register& array,
2758 const Location& pos,
2759 const Register& base) {
2760 // This routine is only used by the SystemArrayCopy intrinsic at the
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002761 // moment. We can allow DataType::Type::kReference as `type` to implement
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002762 // the SystemArrayCopyChar intrinsic.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002763 DCHECK_EQ(type, DataType::Type::kReference);
2764 const int32_t element_size = DataType::Size(type);
2765 const ScaleFactor scale_factor = static_cast<ScaleFactor>(DataType::SizeShift(type));
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002766 const uint32_t data_offset = mirror::Array::DataOffset(element_size).Uint32Value();
2767
2768 if (pos.IsConstant()) {
2769 int32_t constant = pos.GetConstant()->AsIntConstant()->GetValue();
2770 __ leal(base, Address(array, element_size * constant + data_offset));
2771 } else {
2772 __ leal(base, Address(array, pos.AsRegister<Register>(), scale_factor, data_offset));
2773 }
2774}
2775
2776// Compute end source address for the System.arraycopy intrinsic in `end`.
2777static void GenSystemArrayCopyEndAddress(X86Assembler* assembler,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002778 DataType::Type type,
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002779 const Location& copy_length,
2780 const Register& base,
2781 const Register& end) {
2782 // This routine is only used by the SystemArrayCopy intrinsic at the
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002783 // moment. We can allow DataType::Type::kReference as `type` to implement
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002784 // the SystemArrayCopyChar intrinsic.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002785 DCHECK_EQ(type, DataType::Type::kReference);
2786 const int32_t element_size = DataType::Size(type);
2787 const ScaleFactor scale_factor = static_cast<ScaleFactor>(DataType::SizeShift(type));
Roland Levillain9cc0ea82017-03-16 11:25:59 +00002788
2789 if (copy_length.IsConstant()) {
2790 int32_t constant = copy_length.GetConstant()->AsIntConstant()->GetValue();
2791 __ leal(end, Address(base, element_size * constant));
2792 } else {
2793 __ leal(end, Address(base, copy_length.AsRegister<Register>(), scale_factor, 0));
2794 }
2795}
2796
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002797void IntrinsicLocationsBuilderX86::VisitSystemArrayCopy(HInvoke* invoke) {
Roland Levillain0b671c02016-08-19 12:02:34 +01002798 // The only read barrier implementation supporting the
2799 // SystemArrayCopy intrinsic is the Baker-style read barriers.
2800 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002801 return;
2802 }
2803
2804 CodeGenerator::CreateSystemArrayCopyLocationSummary(invoke);
2805 if (invoke->GetLocations() != nullptr) {
2806 // Need a byte register for marking.
2807 invoke->GetLocations()->SetTempAt(1, Location::RegisterLocation(ECX));
2808
2809 static constexpr size_t kSrc = 0;
2810 static constexpr size_t kSrcPos = 1;
2811 static constexpr size_t kDest = 2;
2812 static constexpr size_t kDestPos = 3;
2813 static constexpr size_t kLength = 4;
2814
2815 if (!invoke->InputAt(kSrcPos)->IsIntConstant() &&
2816 !invoke->InputAt(kDestPos)->IsIntConstant() &&
2817 !invoke->InputAt(kLength)->IsIntConstant()) {
2818 if (!IsSameInput(invoke, kSrcPos, kDestPos) &&
2819 !IsSameInput(invoke, kSrcPos, kLength) &&
2820 !IsSameInput(invoke, kDestPos, kLength) &&
2821 !IsSameInput(invoke, kSrc, kDest)) {
2822 // Not enough registers, make the length also take a stack slot.
2823 invoke->GetLocations()->SetInAt(kLength, Location::Any());
2824 }
2825 }
2826 }
2827}
2828
2829void IntrinsicCodeGeneratorX86::VisitSystemArrayCopy(HInvoke* invoke) {
Roland Levillain0b671c02016-08-19 12:02:34 +01002830 // The only read barrier implementation supporting the
2831 // SystemArrayCopy intrinsic is the Baker-style read barriers.
2832 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002833
2834 X86Assembler* assembler = GetAssembler();
2835 LocationSummary* locations = invoke->GetLocations();
2836
2837 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
2838 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
2839 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
2840 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
Roland Levillain0b671c02016-08-19 12:02:34 +01002841 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002842
2843 Register src = locations->InAt(0).AsRegister<Register>();
2844 Location src_pos = locations->InAt(1);
2845 Register dest = locations->InAt(2).AsRegister<Register>();
2846 Location dest_pos = locations->InAt(3);
Roland Levillain0b671c02016-08-19 12:02:34 +01002847 Location length_arg = locations->InAt(4);
2848 Location length = length_arg;
2849 Location temp1_loc = locations->GetTemp(0);
2850 Register temp1 = temp1_loc.AsRegister<Register>();
2851 Location temp2_loc = locations->GetTemp(1);
2852 Register temp2 = temp2_loc.AsRegister<Register>();
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002853
Vladimir Marko174b2e22017-10-12 13:34:49 +01002854 SlowPathCode* intrinsic_slow_path =
2855 new (codegen_->GetScopedAllocator()) IntrinsicSlowPathX86(invoke);
Roland Levillain0b671c02016-08-19 12:02:34 +01002856 codegen_->AddSlowPath(intrinsic_slow_path);
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002857
2858 NearLabel conditions_on_positions_validated;
2859 SystemArrayCopyOptimizations optimizations(invoke);
2860
2861 // If source and destination are the same, we go to slow path if we need to do
2862 // forward copying.
2863 if (src_pos.IsConstant()) {
2864 int32_t src_pos_constant = src_pos.GetConstant()->AsIntConstant()->GetValue();
2865 if (dest_pos.IsConstant()) {
2866 int32_t dest_pos_constant = dest_pos.GetConstant()->AsIntConstant()->GetValue();
2867 if (optimizations.GetDestinationIsSource()) {
2868 // Checked when building locations.
2869 DCHECK_GE(src_pos_constant, dest_pos_constant);
2870 } else if (src_pos_constant < dest_pos_constant) {
2871 __ cmpl(src, dest);
Roland Levillain0b671c02016-08-19 12:02:34 +01002872 __ j(kEqual, intrinsic_slow_path->GetEntryLabel());
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002873 }
2874 } else {
2875 if (!optimizations.GetDestinationIsSource()) {
2876 __ cmpl(src, dest);
2877 __ j(kNotEqual, &conditions_on_positions_validated);
2878 }
2879 __ cmpl(dest_pos.AsRegister<Register>(), Immediate(src_pos_constant));
Roland Levillain0b671c02016-08-19 12:02:34 +01002880 __ j(kGreater, intrinsic_slow_path->GetEntryLabel());
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002881 }
2882 } else {
2883 if (!optimizations.GetDestinationIsSource()) {
2884 __ cmpl(src, dest);
2885 __ j(kNotEqual, &conditions_on_positions_validated);
2886 }
2887 if (dest_pos.IsConstant()) {
2888 int32_t dest_pos_constant = dest_pos.GetConstant()->AsIntConstant()->GetValue();
2889 __ cmpl(src_pos.AsRegister<Register>(), Immediate(dest_pos_constant));
Roland Levillain0b671c02016-08-19 12:02:34 +01002890 __ j(kLess, intrinsic_slow_path->GetEntryLabel());
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002891 } else {
2892 __ cmpl(src_pos.AsRegister<Register>(), dest_pos.AsRegister<Register>());
Roland Levillain0b671c02016-08-19 12:02:34 +01002893 __ j(kLess, intrinsic_slow_path->GetEntryLabel());
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002894 }
2895 }
2896
2897 __ Bind(&conditions_on_positions_validated);
2898
2899 if (!optimizations.GetSourceIsNotNull()) {
2900 // Bail out if the source is null.
2901 __ testl(src, src);
Roland Levillain0b671c02016-08-19 12:02:34 +01002902 __ j(kEqual, intrinsic_slow_path->GetEntryLabel());
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002903 }
2904
2905 if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) {
2906 // Bail out if the destination is null.
2907 __ testl(dest, dest);
Roland Levillain0b671c02016-08-19 12:02:34 +01002908 __ j(kEqual, intrinsic_slow_path->GetEntryLabel());
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002909 }
2910
Roland Levillain0b671c02016-08-19 12:02:34 +01002911 Location temp3_loc = locations->GetTemp(2);
2912 Register temp3 = temp3_loc.AsRegister<Register>();
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002913 if (length.IsStackSlot()) {
2914 __ movl(temp3, Address(ESP, length.GetStackIndex()));
2915 length = Location::RegisterLocation(temp3);
2916 }
2917
2918 // If the length is negative, bail out.
2919 // We have already checked in the LocationsBuilder for the constant case.
2920 if (!length.IsConstant() &&
2921 !optimizations.GetCountIsSourceLength() &&
2922 !optimizations.GetCountIsDestinationLength()) {
2923 __ testl(length.AsRegister<Register>(), length.AsRegister<Register>());
Roland Levillain0b671c02016-08-19 12:02:34 +01002924 __ j(kLess, intrinsic_slow_path->GetEntryLabel());
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002925 }
2926
2927 // Validity checks: source.
2928 CheckPosition(assembler,
2929 src_pos,
2930 src,
2931 length,
Roland Levillain0b671c02016-08-19 12:02:34 +01002932 intrinsic_slow_path,
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002933 temp1,
2934 optimizations.GetCountIsSourceLength());
2935
2936 // Validity checks: dest.
2937 CheckPosition(assembler,
2938 dest_pos,
2939 dest,
2940 length,
Roland Levillain0b671c02016-08-19 12:02:34 +01002941 intrinsic_slow_path,
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002942 temp1,
2943 optimizations.GetCountIsDestinationLength());
2944
2945 if (!optimizations.GetDoesNotNeedTypeCheck()) {
2946 // Check whether all elements of the source array are assignable to the component
2947 // type of the destination array. We do two checks: the classes are the same,
2948 // or the destination is Object[]. If none of these checks succeed, we go to the
2949 // slow path.
Roland Levillain0b671c02016-08-19 12:02:34 +01002950
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002951 if (!optimizations.GetSourceIsNonPrimitiveArray()) {
Roland Levillain0b671c02016-08-19 12:02:34 +01002952 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2953 // /* HeapReference<Class> */ temp1 = src->klass_
2954 codegen_->GenerateFieldLoadWithBakerReadBarrier(
Andreas Gampe3db70682018-12-26 15:12:03 -08002955 invoke, temp1_loc, src, class_offset, /* needs_null_check= */ false);
Roland Levillain0b671c02016-08-19 12:02:34 +01002956 // Bail out if the source is not a non primitive array.
2957 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2958 codegen_->GenerateFieldLoadWithBakerReadBarrier(
Andreas Gampe3db70682018-12-26 15:12:03 -08002959 invoke, temp1_loc, temp1, component_offset, /* needs_null_check= */ false);
Roland Levillain0b671c02016-08-19 12:02:34 +01002960 __ testl(temp1, temp1);
2961 __ j(kEqual, intrinsic_slow_path->GetEntryLabel());
2962 // If heap poisoning is enabled, `temp1` has been unpoisoned
2963 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
2964 } else {
2965 // /* HeapReference<Class> */ temp1 = src->klass_
2966 __ movl(temp1, Address(src, class_offset));
2967 __ MaybeUnpoisonHeapReference(temp1);
2968 // Bail out if the source is not a non primitive array.
2969 // /* HeapReference<Class> */ temp1 = temp1->component_type_
2970 __ movl(temp1, Address(temp1, component_offset));
2971 __ testl(temp1, temp1);
2972 __ j(kEqual, intrinsic_slow_path->GetEntryLabel());
2973 __ MaybeUnpoisonHeapReference(temp1);
2974 }
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002975 __ cmpw(Address(temp1, primitive_offset), Immediate(Primitive::kPrimNot));
Roland Levillain0b671c02016-08-19 12:02:34 +01002976 __ j(kNotEqual, intrinsic_slow_path->GetEntryLabel());
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002977 }
2978
Roland Levillain0b671c02016-08-19 12:02:34 +01002979 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2980 if (length.Equals(Location::RegisterLocation(temp3))) {
2981 // When Baker read barriers are enabled, register `temp3`,
2982 // which in the present case contains the `length` parameter,
2983 // will be overwritten below. Make the `length` location
2984 // reference the original stack location; it will be moved
2985 // back to `temp3` later if necessary.
2986 DCHECK(length_arg.IsStackSlot());
2987 length = length_arg;
2988 }
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002989
Roland Levillain0b671c02016-08-19 12:02:34 +01002990 // /* HeapReference<Class> */ temp1 = dest->klass_
2991 codegen_->GenerateFieldLoadWithBakerReadBarrier(
Andreas Gampe3db70682018-12-26 15:12:03 -08002992 invoke, temp1_loc, dest, class_offset, /* needs_null_check= */ false);
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01002993
Roland Levillain0b671c02016-08-19 12:02:34 +01002994 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
2995 // Bail out if the destination is not a non primitive array.
2996 //
2997 // Register `temp1` is not trashed by the read barrier emitted
2998 // by GenerateFieldLoadWithBakerReadBarrier below, as that
2999 // method produces a call to a ReadBarrierMarkRegX entry point,
3000 // which saves all potentially live registers, including
3001 // temporaries such a `temp1`.
3002 // /* HeapReference<Class> */ temp2 = temp1->component_type_
3003 codegen_->GenerateFieldLoadWithBakerReadBarrier(
Andreas Gampe3db70682018-12-26 15:12:03 -08003004 invoke, temp2_loc, temp1, component_offset, /* needs_null_check= */ false);
Roland Levillain0b671c02016-08-19 12:02:34 +01003005 __ testl(temp2, temp2);
3006 __ j(kEqual, intrinsic_slow_path->GetEntryLabel());
3007 // If heap poisoning is enabled, `temp2` has been unpoisoned
3008 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
3009 __ cmpw(Address(temp2, primitive_offset), Immediate(Primitive::kPrimNot));
3010 __ j(kNotEqual, intrinsic_slow_path->GetEntryLabel());
3011 }
3012
3013 // For the same reason given earlier, `temp1` is not trashed by the
3014 // read barrier emitted by GenerateFieldLoadWithBakerReadBarrier below.
3015 // /* HeapReference<Class> */ temp2 = src->klass_
3016 codegen_->GenerateFieldLoadWithBakerReadBarrier(
Andreas Gampe3db70682018-12-26 15:12:03 -08003017 invoke, temp2_loc, src, class_offset, /* needs_null_check= */ false);
Roland Levillain0b671c02016-08-19 12:02:34 +01003018 // Note: if heap poisoning is on, we are comparing two unpoisoned references here.
3019 __ cmpl(temp1, temp2);
3020
3021 if (optimizations.GetDestinationIsTypedObjectArray()) {
3022 NearLabel do_copy;
3023 __ j(kEqual, &do_copy);
3024 // /* HeapReference<Class> */ temp1 = temp1->component_type_
3025 codegen_->GenerateFieldLoadWithBakerReadBarrier(
Andreas Gampe3db70682018-12-26 15:12:03 -08003026 invoke, temp1_loc, temp1, component_offset, /* needs_null_check= */ false);
Roland Levillain0b671c02016-08-19 12:02:34 +01003027 // We do not need to emit a read barrier for the following
3028 // heap reference load, as `temp1` is only used in a
3029 // comparison with null below, and this reference is not
3030 // kept afterwards.
3031 __ cmpl(Address(temp1, super_offset), Immediate(0));
3032 __ j(kNotEqual, intrinsic_slow_path->GetEntryLabel());
3033 __ Bind(&do_copy);
3034 } else {
3035 __ j(kNotEqual, intrinsic_slow_path->GetEntryLabel());
3036 }
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01003037 } else {
Roland Levillain0b671c02016-08-19 12:02:34 +01003038 // Non read barrier code.
3039
3040 // /* HeapReference<Class> */ temp1 = dest->klass_
3041 __ movl(temp1, Address(dest, class_offset));
3042 if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
3043 __ MaybeUnpoisonHeapReference(temp1);
3044 // Bail out if the destination is not a non primitive array.
3045 // /* HeapReference<Class> */ temp2 = temp1->component_type_
3046 __ movl(temp2, Address(temp1, component_offset));
3047 __ testl(temp2, temp2);
3048 __ j(kEqual, intrinsic_slow_path->GetEntryLabel());
3049 __ MaybeUnpoisonHeapReference(temp2);
3050 __ cmpw(Address(temp2, primitive_offset), Immediate(Primitive::kPrimNot));
3051 __ j(kNotEqual, intrinsic_slow_path->GetEntryLabel());
3052 // Re-poison the heap reference to make the compare instruction below
3053 // compare two poisoned references.
3054 __ PoisonHeapReference(temp1);
3055 }
3056
3057 // Note: if heap poisoning is on, we are comparing two poisoned references here.
3058 __ cmpl(temp1, Address(src, class_offset));
3059
3060 if (optimizations.GetDestinationIsTypedObjectArray()) {
3061 NearLabel do_copy;
3062 __ j(kEqual, &do_copy);
3063 __ MaybeUnpoisonHeapReference(temp1);
3064 // /* HeapReference<Class> */ temp1 = temp1->component_type_
3065 __ movl(temp1, Address(temp1, component_offset));
3066 __ MaybeUnpoisonHeapReference(temp1);
3067 __ cmpl(Address(temp1, super_offset), Immediate(0));
3068 __ j(kNotEqual, intrinsic_slow_path->GetEntryLabel());
3069 __ Bind(&do_copy);
3070 } else {
3071 __ j(kNotEqual, intrinsic_slow_path->GetEntryLabel());
3072 }
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01003073 }
3074 } else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
3075 DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
3076 // Bail out if the source is not a non primitive array.
Roland Levillain0b671c02016-08-19 12:02:34 +01003077 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
3078 // /* HeapReference<Class> */ temp1 = src->klass_
3079 codegen_->GenerateFieldLoadWithBakerReadBarrier(
Andreas Gampe3db70682018-12-26 15:12:03 -08003080 invoke, temp1_loc, src, class_offset, /* needs_null_check= */ false);
Roland Levillain0b671c02016-08-19 12:02:34 +01003081 // /* HeapReference<Class> */ temp1 = temp1->component_type_
3082 codegen_->GenerateFieldLoadWithBakerReadBarrier(
Andreas Gampe3db70682018-12-26 15:12:03 -08003083 invoke, temp1_loc, temp1, component_offset, /* needs_null_check= */ false);
Roland Levillain0b671c02016-08-19 12:02:34 +01003084 __ testl(temp1, temp1);
3085 __ j(kEqual, intrinsic_slow_path->GetEntryLabel());
3086 // If heap poisoning is enabled, `temp1` has been unpoisoned
3087 // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
3088 } else {
3089 // /* HeapReference<Class> */ temp1 = src->klass_
3090 __ movl(temp1, Address(src, class_offset));
3091 __ MaybeUnpoisonHeapReference(temp1);
3092 // /* HeapReference<Class> */ temp1 = temp1->component_type_
3093 __ movl(temp1, Address(temp1, component_offset));
3094 __ testl(temp1, temp1);
3095 __ j(kEqual, intrinsic_slow_path->GetEntryLabel());
3096 __ MaybeUnpoisonHeapReference(temp1);
3097 }
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01003098 __ cmpw(Address(temp1, primitive_offset), Immediate(Primitive::kPrimNot));
Roland Levillain0b671c02016-08-19 12:02:34 +01003099 __ j(kNotEqual, intrinsic_slow_path->GetEntryLabel());
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01003100 }
3101
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003102 const DataType::Type type = DataType::Type::kReference;
3103 const int32_t element_size = DataType::Size(type);
Roland Levillain9cc0ea82017-03-16 11:25:59 +00003104
Roland Levillain0b671c02016-08-19 12:02:34 +01003105 // Compute the base source address in `temp1`.
Roland Levillain9cc0ea82017-03-16 11:25:59 +00003106 GenSystemArrayCopyBaseAddress(GetAssembler(), type, src, src_pos, temp1);
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01003107
Roland Levillain0b671c02016-08-19 12:02:34 +01003108 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
3109 // If it is needed (in the case of the fast-path loop), the base
3110 // destination address is computed later, as `temp2` is used for
3111 // intermediate computations.
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01003112
Roland Levillain0b671c02016-08-19 12:02:34 +01003113 // Compute the end source address in `temp3`.
Roland Levillain9cc0ea82017-03-16 11:25:59 +00003114 if (length.IsStackSlot()) {
3115 // Location `length` is again pointing at a stack slot, as
3116 // register `temp3` (which was containing the length parameter
3117 // earlier) has been overwritten; restore it now
3118 DCHECK(length.Equals(length_arg));
3119 __ movl(temp3, Address(ESP, length.GetStackIndex()));
3120 length = Location::RegisterLocation(temp3);
Roland Levillain0b671c02016-08-19 12:02:34 +01003121 }
Roland Levillain9cc0ea82017-03-16 11:25:59 +00003122 GenSystemArrayCopyEndAddress(GetAssembler(), type, length, temp1, temp3);
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01003123
Roland Levillain0b671c02016-08-19 12:02:34 +01003124 // SystemArrayCopy implementation for Baker read barriers (see
3125 // also CodeGeneratorX86::GenerateReferenceLoadWithBakerReadBarrier):
3126 //
3127 // if (src_ptr != end_ptr) {
3128 // uint32_t rb_state = Lockword(src->monitor_).ReadBarrierState();
3129 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
Hiroshi Yamauchi12b58b22016-11-01 11:55:29 -07003130 // bool is_gray = (rb_state == ReadBarrier::GrayState());
Roland Levillain0b671c02016-08-19 12:02:34 +01003131 // if (is_gray) {
3132 // // Slow-path copy.
3133 // for (size_t i = 0; i != length; ++i) {
3134 // dest_array[dest_pos + i] =
3135 // MaybePoison(ReadBarrier::Mark(MaybeUnpoison(src_array[src_pos + i])));
3136 // }
3137 // } else {
3138 // // Fast-path copy.
3139 // do {
3140 // *dest_ptr++ = *src_ptr++;
3141 // } while (src_ptr != end_ptr)
3142 // }
3143 // }
3144
3145 NearLabel loop, done;
3146
3147 // Don't enter copy loop if `length == 0`.
3148 __ cmpl(temp1, temp3);
3149 __ j(kEqual, &done);
3150
Vladimir Marko953437b2016-08-24 08:30:46 +00003151 // Given the numeric representation, it's enough to check the low bit of the rb_state.
Roland Levillain14e5a292018-06-28 12:00:56 +01003152 static_assert(ReadBarrier::NonGrayState() == 0, "Expecting non-gray to have value 0");
Hiroshi Yamauchi12b58b22016-11-01 11:55:29 -07003153 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
Vladimir Marko953437b2016-08-24 08:30:46 +00003154 constexpr uint32_t gray_byte_position = LockWord::kReadBarrierStateShift / kBitsPerByte;
3155 constexpr uint32_t gray_bit_position = LockWord::kReadBarrierStateShift % kBitsPerByte;
3156 constexpr int32_t test_value = static_cast<int8_t>(1 << gray_bit_position);
3157
Hiroshi Yamauchi12b58b22016-11-01 11:55:29 -07003158 // if (rb_state == ReadBarrier::GrayState())
Vladimir Marko953437b2016-08-24 08:30:46 +00003159 // goto slow_path;
3160 // At this point, just do the "if" and make sure that flags are preserved until the branch.
3161 __ testb(Address(src, monitor_offset + gray_byte_position), Immediate(test_value));
Roland Levillain0b671c02016-08-19 12:02:34 +01003162
3163 // Load fence to prevent load-load reordering.
3164 // Note that this is a no-op, thanks to the x86 memory model.
3165 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
3166
3167 // Slow path used to copy array when `src` is gray.
3168 SlowPathCode* read_barrier_slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01003169 new (codegen_->GetScopedAllocator()) ReadBarrierSystemArrayCopySlowPathX86(invoke);
Roland Levillain0b671c02016-08-19 12:02:34 +01003170 codegen_->AddSlowPath(read_barrier_slow_path);
3171
Vladimir Marko953437b2016-08-24 08:30:46 +00003172 // We have done the "if" of the gray bit check above, now branch based on the flags.
3173 __ j(kNotZero, read_barrier_slow_path->GetEntryLabel());
Roland Levillain0b671c02016-08-19 12:02:34 +01003174
3175 // Fast-path copy.
Roland Levillain9cc0ea82017-03-16 11:25:59 +00003176 // Compute the base destination address in `temp2`.
3177 GenSystemArrayCopyBaseAddress(GetAssembler(), type, dest, dest_pos, temp2);
Roland Levillain0b671c02016-08-19 12:02:34 +01003178 // Iterate over the arrays and do a raw copy of the objects. We don't need to
3179 // poison/unpoison.
3180 __ Bind(&loop);
3181 __ pushl(Address(temp1, 0));
3182 __ cfi().AdjustCFAOffset(4);
3183 __ popl(Address(temp2, 0));
3184 __ cfi().AdjustCFAOffset(-4);
3185 __ addl(temp1, Immediate(element_size));
3186 __ addl(temp2, Immediate(element_size));
3187 __ cmpl(temp1, temp3);
3188 __ j(kNotEqual, &loop);
3189
3190 __ Bind(read_barrier_slow_path->GetExitLabel());
3191 __ Bind(&done);
3192 } else {
3193 // Non read barrier code.
Roland Levillain0b671c02016-08-19 12:02:34 +01003194 // Compute the base destination address in `temp2`.
Roland Levillain9cc0ea82017-03-16 11:25:59 +00003195 GenSystemArrayCopyBaseAddress(GetAssembler(), type, dest, dest_pos, temp2);
Roland Levillain0b671c02016-08-19 12:02:34 +01003196 // Compute the end source address in `temp3`.
Roland Levillain9cc0ea82017-03-16 11:25:59 +00003197 GenSystemArrayCopyEndAddress(GetAssembler(), type, length, temp1, temp3);
Roland Levillain0b671c02016-08-19 12:02:34 +01003198 // Iterate over the arrays and do a raw copy of the objects. We don't need to
3199 // poison/unpoison.
3200 NearLabel loop, done;
3201 __ cmpl(temp1, temp3);
3202 __ j(kEqual, &done);
3203 __ Bind(&loop);
3204 __ pushl(Address(temp1, 0));
3205 __ cfi().AdjustCFAOffset(4);
3206 __ popl(Address(temp2, 0));
3207 __ cfi().AdjustCFAOffset(-4);
3208 __ addl(temp1, Immediate(element_size));
3209 __ addl(temp2, Immediate(element_size));
3210 __ cmpl(temp1, temp3);
3211 __ j(kNotEqual, &loop);
3212 __ Bind(&done);
3213 }
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01003214
3215 // We only need one card marking on the destination array.
Andreas Gampe3db70682018-12-26 15:12:03 -08003216 codegen_->MarkGCCard(temp1, temp2, dest, Register(kNoRegister), /* value_can_be_null= */ false);
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01003217
Roland Levillain0b671c02016-08-19 12:02:34 +01003218 __ Bind(intrinsic_slow_path->GetExitLabel());
Nicolas Geoffrayfea1abd2016-07-06 12:09:12 +01003219}
3220
Vladimir Marko01b65522020-10-28 15:43:54 +00003221static void RequestBaseMethodAddressInRegister(HInvoke* invoke) {
3222 LocationSummary* locations = invoke->GetLocations();
3223 if (locations != nullptr) {
3224 HInvokeStaticOrDirect* invoke_static_or_direct = invoke->AsInvokeStaticOrDirect();
3225 // Note: The base method address is not present yet when this is called from the
3226 // PCRelativeHandlerVisitor via IsCallFreeIntrinsic() to determine whether to insert it.
3227 if (invoke_static_or_direct->HasSpecialInput()) {
3228 DCHECK(invoke_static_or_direct->InputAt(invoke_static_or_direct->GetSpecialInputIndex())
3229 ->IsX86ComputeBaseMethodAddress());
3230 locations->SetInAt(invoke_static_or_direct->GetSpecialInputIndex(),
3231 Location::RequiresRegister());
3232 }
3233 }
3234}
3235
Nicolas Geoffray331605a2017-03-01 11:01:41 +00003236void IntrinsicLocationsBuilderX86::VisitIntegerValueOf(HInvoke* invoke) {
Vladimir Markoeebb8212018-06-05 14:57:24 +01003237 DCHECK(invoke->IsInvokeStaticOrDirect());
Nicolas Geoffray331605a2017-03-01 11:01:41 +00003238 InvokeRuntimeCallingConvention calling_convention;
3239 IntrinsicVisitor::ComputeIntegerValueOfLocations(
3240 invoke,
3241 codegen_,
3242 Location::RegisterLocation(EAX),
3243 Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
Vladimir Marko01b65522020-10-28 15:43:54 +00003244 RequestBaseMethodAddressInRegister(invoke);
Nicolas Geoffray331605a2017-03-01 11:01:41 +00003245}
3246
3247void IntrinsicCodeGeneratorX86::VisitIntegerValueOf(HInvoke* invoke) {
Vladimir Markoeebb8212018-06-05 14:57:24 +01003248 DCHECK(invoke->IsInvokeStaticOrDirect());
Vladimir Marko6fd16062018-06-26 11:02:04 +01003249 IntrinsicVisitor::IntegerValueOfInfo info =
3250 IntrinsicVisitor::ComputeIntegerValueOfInfo(invoke, codegen_->GetCompilerOptions());
Nicolas Geoffray331605a2017-03-01 11:01:41 +00003251 LocationSummary* locations = invoke->GetLocations();
3252 X86Assembler* assembler = GetAssembler();
3253
3254 Register out = locations->Out().AsRegister<Register>();
Vladimir Markode91ca92020-10-27 13:41:40 +00003255 auto allocate_instance = [&]() {
3256 DCHECK_EQ(out, InvokeRuntimeCallingConvention().GetRegisterAt(0));
3257 codegen_->LoadIntrinsicDeclaringClass(out, invoke->AsInvokeStaticOrDirect());
3258 codegen_->InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
3259 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
3260 };
Nicolas Geoffray331605a2017-03-01 11:01:41 +00003261 if (invoke->InputAt(0)->IsConstant()) {
3262 int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue();
Vladimir Marko6fd16062018-06-26 11:02:04 +01003263 if (static_cast<uint32_t>(value - info.low) < info.length) {
Nicolas Geoffray331605a2017-03-01 11:01:41 +00003264 // Just embed the j.l.Integer in the code.
Vladimir Marko6fd16062018-06-26 11:02:04 +01003265 DCHECK_NE(info.value_boot_image_reference, IntegerValueOfInfo::kInvalidReference);
Vladimir Markoeebb8212018-06-05 14:57:24 +01003266 codegen_->LoadBootImageAddress(
Vladimir Marko6fd16062018-06-26 11:02:04 +01003267 out, info.value_boot_image_reference, invoke->AsInvokeStaticOrDirect());
Nicolas Geoffray331605a2017-03-01 11:01:41 +00003268 } else {
Vladimir Markoeebb8212018-06-05 14:57:24 +01003269 DCHECK(locations->CanCall());
Nicolas Geoffray331605a2017-03-01 11:01:41 +00003270 // Allocate and initialize a new j.l.Integer.
3271 // TODO: If we JIT, we could allocate the j.l.Integer now, and store it in the
3272 // JIT object table.
Vladimir Markode91ca92020-10-27 13:41:40 +00003273 allocate_instance();
Nicolas Geoffray331605a2017-03-01 11:01:41 +00003274 __ movl(Address(out, info.value_offset), Immediate(value));
3275 }
3276 } else {
Vladimir Markoeebb8212018-06-05 14:57:24 +01003277 DCHECK(locations->CanCall());
Nicolas Geoffray331605a2017-03-01 11:01:41 +00003278 Register in = locations->InAt(0).AsRegister<Register>();
3279 // Check bounds of our cache.
3280 __ leal(out, Address(in, -info.low));
Vladimir Markoeebb8212018-06-05 14:57:24 +01003281 __ cmpl(out, Immediate(info.length));
Nicolas Geoffray331605a2017-03-01 11:01:41 +00003282 NearLabel allocate, done;
3283 __ j(kAboveEqual, &allocate);
3284 // If the value is within the bounds, load the j.l.Integer directly from the array.
Vladimir Markoeebb8212018-06-05 14:57:24 +01003285 constexpr size_t kElementSize = sizeof(mirror::HeapReference<mirror::Object>);
Vladimir Markoeebb8212018-06-05 14:57:24 +01003286 static_assert((1u << TIMES_4) == sizeof(mirror::HeapReference<mirror::Object>),
3287 "Check heap reference size.");
Vladimir Marko6fd16062018-06-26 11:02:04 +01003288 if (codegen_->GetCompilerOptions().IsBootImage()) {
3289 DCHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u);
3290 size_t method_address_index = invoke->AsInvokeStaticOrDirect()->GetSpecialInputIndex();
3291 HX86ComputeBaseMethodAddress* method_address =
3292 invoke->InputAt(method_address_index)->AsX86ComputeBaseMethodAddress();
3293 DCHECK(method_address != nullptr);
3294 Register method_address_reg =
3295 invoke->GetLocations()->InAt(method_address_index).AsRegister<Register>();
Vladimir Marko4ef451a2020-07-23 09:54:27 +00003296 __ movl(out,
3297 Address(method_address_reg, out, TIMES_4, CodeGeneratorX86::kPlaceholder32BitOffset));
Vladimir Marko6fd16062018-06-26 11:02:04 +01003298 codegen_->RecordBootImageIntrinsicPatch(method_address, info.array_data_boot_image_reference);
3299 } else {
3300 // Note: We're about to clobber the index in `out`, so we need to use `in` and
3301 // adjust the offset accordingly.
3302 uint32_t mid_array_boot_image_offset =
3303 info.array_data_boot_image_reference - info.low * kElementSize;
3304 codegen_->LoadBootImageAddress(
3305 out, mid_array_boot_image_offset, invoke->AsInvokeStaticOrDirect());
3306 DCHECK_NE(out, in);
3307 __ movl(out, Address(out, in, TIMES_4, 0));
3308 }
Nicolas Geoffray331605a2017-03-01 11:01:41 +00003309 __ MaybeUnpoisonHeapReference(out);
3310 __ jmp(&done);
3311 __ Bind(&allocate);
3312 // Otherwise allocate and initialize a new j.l.Integer.
Vladimir Markode91ca92020-10-27 13:41:40 +00003313 allocate_instance();
Nicolas Geoffray331605a2017-03-01 11:01:41 +00003314 __ movl(Address(out, info.value_offset), in);
3315 __ Bind(&done);
3316 }
3317}
3318
Vladimir Marko01b65522020-10-28 15:43:54 +00003319void IntrinsicLocationsBuilderX86::VisitReferenceGetReferent(HInvoke* invoke) {
3320 IntrinsicVisitor::CreateReferenceGetReferentLocations(invoke, codegen_);
3321 RequestBaseMethodAddressInRegister(invoke);
3322}
3323
3324void IntrinsicCodeGeneratorX86::VisitReferenceGetReferent(HInvoke* invoke) {
3325 X86Assembler* assembler = GetAssembler();
3326 LocationSummary* locations = invoke->GetLocations();
3327
3328 Location obj = locations->InAt(0);
3329 Location out = locations->Out();
3330
3331 SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathX86(invoke);
3332 codegen_->AddSlowPath(slow_path);
3333
3334 if (kEmitCompilerReadBarrier) {
3335 // Check self->GetWeakRefAccessEnabled().
3336 ThreadOffset32 offset = Thread::WeakRefAccessEnabledOffset<kX86PointerSize>();
3337 __ fs()->cmpl(Address::Absolute(offset), Immediate(0));
3338 __ j(kEqual, slow_path->GetEntryLabel());
3339 }
3340
3341 // Load the java.lang.ref.Reference class, use the output register as a temporary.
3342 codegen_->LoadIntrinsicDeclaringClass(out.AsRegister<Register>(),
3343 invoke->AsInvokeStaticOrDirect());
3344
3345 // Check static fields java.lang.ref.Reference.{disableIntrinsic,slowPathEnabled} together.
3346 MemberOffset disable_intrinsic_offset = IntrinsicVisitor::GetReferenceDisableIntrinsicOffset();
3347 DCHECK_ALIGNED(disable_intrinsic_offset.Uint32Value(), 2u);
3348 DCHECK_EQ(disable_intrinsic_offset.Uint32Value() + 1u,
3349 IntrinsicVisitor::GetReferenceSlowPathEnabledOffset().Uint32Value());
3350 __ cmpw(Address(out.AsRegister<Register>(), disable_intrinsic_offset.Uint32Value()),
3351 Immediate(0));
3352 __ j(kNotEqual, slow_path->GetEntryLabel());
3353
3354 // Load the value from the field.
3355 uint32_t referent_offset = mirror::Reference::ReferentOffset().Uint32Value();
3356 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
3357 codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
3358 out,
3359 obj.AsRegister<Register>(),
3360 referent_offset,
3361 /*needs_null_check=*/ true);
3362 // Note that the fence is a no-op, thanks to the x86 memory model.
3363 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny); // `referent` is volatile.
3364 } else {
3365 __ movl(out.AsRegister<Register>(), Address(obj.AsRegister<Register>(), referent_offset));
3366 codegen_->MaybeRecordImplicitNullCheck(invoke);
3367 // Note that the fence is a no-op, thanks to the x86 memory model.
3368 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny); // `referent` is volatile.
3369 codegen_->MaybeGenerateReadBarrierSlow(invoke, out, out, obj, referent_offset);
3370 }
3371 __ Bind(slow_path->GetExitLabel());
3372}
3373
Vladimir Markoac27ac02021-02-01 09:31:02 +00003374void IntrinsicLocationsBuilderX86::VisitReferenceRefersTo(HInvoke* invoke) {
3375 IntrinsicVisitor::CreateReferenceRefersToLocations(invoke);
3376}
3377
3378void IntrinsicCodeGeneratorX86::VisitReferenceRefersTo(HInvoke* invoke) {
3379 X86Assembler* assembler = GetAssembler();
3380 LocationSummary* locations = invoke->GetLocations();
3381
3382 Register obj = locations->InAt(0).AsRegister<Register>();
3383 Register other = locations->InAt(1).AsRegister<Register>();
3384 Register out = locations->Out().AsRegister<Register>();
3385
3386 uint32_t referent_offset = mirror::Reference::ReferentOffset().Uint32Value();
3387 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
3388
3389 __ movl(out, Address(obj, referent_offset));
3390 codegen_->MaybeRecordImplicitNullCheck(invoke);
Vladimir Markoa0a20cd2021-02-05 15:55:47 +00003391 __ MaybeUnpoisonHeapReference(out);
Vladimir Markoac27ac02021-02-01 09:31:02 +00003392 // Note that the fence is a no-op, thanks to the x86 memory model.
3393 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny); // `referent` is volatile.
3394
3395 NearLabel end, return_true, return_false;
3396 __ cmpl(out, other);
3397
3398 if (kEmitCompilerReadBarrier) {
3399 DCHECK(kUseBakerReadBarrier);
3400
3401 __ j(kEqual, &return_true);
3402
3403 // Check if the loaded reference is null.
3404 __ testl(out, out);
3405 __ j(kZero, &return_false);
3406
3407 // For correct memory visibility, we need a barrier before loading the lock word
3408 // but we already have the barrier emitted for volatile load above which is sufficient.
3409
3410 // Load the lockword and check if it is a forwarding address.
3411 static_assert(LockWord::kStateShift == 30u);
3412 static_assert(LockWord::kStateForwardingAddress == 3u);
3413 __ movl(out, Address(out, monitor_offset));
3414 __ cmpl(out, Immediate(static_cast<int32_t>(0xc0000000)));
3415 __ j(kBelow, &return_false);
3416
3417 // Extract the forwarding address and compare with `other`.
3418 __ shll(out, Immediate(LockWord::kForwardingAddressShift));
3419 __ cmpl(out, other);
3420 }
3421
3422 __ j(kNotEqual, &return_false);
3423
3424 // Return true and exit the function.
3425 __ Bind(&return_true);
3426 __ movl(out, Immediate(1));
3427 __ jmp(&end);
3428
3429 // Return false and exit the function.
3430 __ Bind(&return_false);
3431 __ xorl(out, out);
3432 __ Bind(&end);
3433}
3434
Nicolas Geoffray365719c2017-03-08 13:11:50 +00003435void IntrinsicLocationsBuilderX86::VisitThreadInterrupted(HInvoke* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01003436 LocationSummary* locations =
3437 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
Nicolas Geoffray365719c2017-03-08 13:11:50 +00003438 locations->SetOut(Location::RequiresRegister());
3439}
3440
3441void IntrinsicCodeGeneratorX86::VisitThreadInterrupted(HInvoke* invoke) {
3442 X86Assembler* assembler = GetAssembler();
3443 Register out = invoke->GetLocations()->Out().AsRegister<Register>();
3444 Address address = Address::Absolute(Thread::InterruptedOffset<kX86PointerSize>().Int32Value());
3445 NearLabel done;
3446 __ fs()->movl(out, address);
3447 __ testl(out, out);
3448 __ j(kEqual, &done);
3449 __ fs()->movl(address, Immediate(0));
3450 codegen_->MemoryFence();
3451 __ Bind(&done);
3452}
3453
Hans Boehmc7b28de2018-03-09 17:05:28 -08003454void IntrinsicLocationsBuilderX86::VisitReachabilityFence(HInvoke* invoke) {
3455 LocationSummary* locations =
3456 new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
3457 locations->SetInAt(0, Location::Any());
3458}
3459
3460void IntrinsicCodeGeneratorX86::VisitReachabilityFence(HInvoke* invoke ATTRIBUTE_UNUSED) { }
Nicolas Geoffray365719c2017-03-08 13:11:50 +00003461
Andra Danciudc787f42020-07-07 14:28:56 +00003462void IntrinsicLocationsBuilderX86::VisitIntegerDivideUnsigned(HInvoke* invoke) {
3463 LocationSummary* locations = new (allocator_) LocationSummary(invoke,
3464 LocationSummary::kCallOnSlowPath,
3465 kIntrinsified);
3466 locations->SetInAt(0, Location::RegisterLocation(EAX));
3467 locations->SetInAt(1, Location::RequiresRegister());
3468 locations->SetOut(Location::SameAsFirstInput());
3469 // Intel uses edx:eax as the dividend.
3470 locations->AddTemp(Location::RegisterLocation(EDX));
3471}
3472
3473void IntrinsicCodeGeneratorX86::VisitIntegerDivideUnsigned(HInvoke* invoke) {
3474 X86Assembler* assembler = GetAssembler();
3475 LocationSummary* locations = invoke->GetLocations();
3476 Location out = locations->Out();
3477 Location first = locations->InAt(0);
3478 Location second = locations->InAt(1);
3479 Register edx = locations->GetTemp(0).AsRegister<Register>();
3480 Register second_reg = second.AsRegister<Register>();
3481
3482 DCHECK_EQ(EAX, first.AsRegister<Register>());
3483 DCHECK_EQ(EAX, out.AsRegister<Register>());
3484 DCHECK_EQ(EDX, edx);
3485
3486 // Check if divisor is zero, bail to managed implementation to handle.
3487 __ testl(second_reg, second_reg);
3488 SlowPathCode* slow_path = new (codegen_->GetScopedAllocator()) IntrinsicSlowPathX86(invoke);
3489 codegen_->AddSlowPath(slow_path);
3490 __ j(kEqual, slow_path->GetEntryLabel());
3491
3492 __ xorl(edx, edx);
3493 __ divl(second_reg);
3494
3495 __ Bind(slow_path->GetExitLabel());
3496}
3497
Ulyana Trafimovich98f01d12021-07-28 14:33:34 +00003498static bool HasVarHandleIntrinsicImplementation(HInvoke* invoke) {
3499 VarHandleOptimizations optimizations(invoke);
3500 if (optimizations.GetDoNotIntrinsify()) {
3501 return false;
3502 }
3503
Vladimir Marko4a889b72021-07-21 16:20:54 +00003504 size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke);
Ulyana Trafimovich98f01d12021-07-28 14:33:34 +00003505 DCHECK_LE(expected_coordinates_count, 2u); // Filtered by the `DoNotIntrinsify` flag above.
Vladimir Marko4a889b72021-07-21 16:20:54 +00003506 if (expected_coordinates_count > 1u) {
3507 // Only static and instance fields VarHandle are supported now.
Ulyana Trafimovich98f01d12021-07-28 14:33:34 +00003508 // TODO: add support for arrays and views.
Vladimir Markob3a7a6a2021-07-20 15:02:33 +01003509 return false;
3510 }
3511
Andra Danciu5e13d452020-09-08 14:35:09 +00003512 return true;
3513}
3514
Andra Danciu73c31802020-09-01 13:17:05 +00003515static void GenerateVarHandleAccessModeCheck(Register varhandle_object,
3516 mirror::VarHandle::AccessMode access_mode,
3517 SlowPathCode* slow_path,
3518 X86Assembler* assembler) {
3519 const uint32_t access_modes_bitmask_offset =
3520 mirror::VarHandle::AccessModesBitMaskOffset().Uint32Value();
3521 const uint32_t access_mode_bit = 1u << static_cast<uint32_t>(access_mode);
3522
3523 // If the access mode is not supported, bail to runtime implementation to handle
3524 __ testl(Address(varhandle_object, access_modes_bitmask_offset), Immediate(access_mode_bit));
Andra Danciu63c0c2d2020-09-07 15:50:40 +00003525 __ j(kZero, slow_path->GetEntryLabel());
Andra Danciu73c31802020-09-01 13:17:05 +00003526}
3527
3528static void GenerateVarHandleStaticFieldCheck(Register varhandle_object,
3529 SlowPathCode* slow_path,
3530 X86Assembler* assembler) {
3531 const uint32_t coordtype0_offset = mirror::VarHandle::CoordinateType0Offset().Uint32Value();
3532
3533 // Check that the VarHandle references a static field by checking that coordinateType0 == null.
3534 // Do not emit read barrier (or unpoison the reference) for comparing to null.
3535 __ cmpl(Address(varhandle_object, coordtype0_offset), Immediate(0));
3536 __ j(kNotEqual, slow_path->GetEntryLabel());
3537}
3538
3539static void GenerateSubTypeObjectCheck(Register object,
3540 Register temp,
3541 Address type_address,
3542 SlowPathCode* slow_path,
Andra Danciu63c0c2d2020-09-07 15:50:40 +00003543 X86Assembler* assembler,
3544 bool object_can_be_null = true) {
Andra Danciu73c31802020-09-01 13:17:05 +00003545 const uint32_t class_offset = mirror::Object::ClassOffset().Uint32Value();
3546 const uint32_t super_class_offset = mirror::Class::SuperClassOffset().Uint32Value();
3547 NearLabel check_type_compatibility, type_matched;
3548
Andra Danciu63c0c2d2020-09-07 15:50:40 +00003549 // If the object is null, there is no need to check the type
3550 if (object_can_be_null) {
3551 __ testl(object, object);
3552 __ j(kZero, &type_matched);
3553 }
3554
Andra Danciu73c31802020-09-01 13:17:05 +00003555 // Do not unpoison for in-memory comparison.
3556 // We deliberately avoid the read barrier, letting the slow path handle the false negatives.
3557 __ movl(temp, Address(object, class_offset));
3558 __ Bind(&check_type_compatibility);
3559 __ cmpl(temp, type_address);
3560 __ j(kEqual, &type_matched);
3561 // Load the super class.
3562 __ MaybeUnpoisonHeapReference(temp);
3563 __ movl(temp, Address(temp, super_class_offset));
3564 // If the super class is null, we reached the root of the hierarchy without a match.
3565 // We let the slow path handle uncovered cases (e.g. interfaces).
3566 __ testl(temp, temp);
3567 __ j(kEqual, slow_path->GetEntryLabel());
3568 __ jmp(&check_type_compatibility);
3569 __ Bind(&type_matched);
3570}
3571
Ulyana Trafimovich98f01d12021-07-28 14:33:34 +00003572static void GenerateVarHandleInstanceFieldChecks(HInvoke* invoke,
3573 Register temp,
3574 SlowPathCode* slow_path,
3575 X86Assembler* assembler) {
3576 VarHandleOptimizations optimizations(invoke);
3577 LocationSummary* locations = invoke->GetLocations();
3578 Register varhandle_object = locations->InAt(0).AsRegister<Register>();
3579 Register object = locations->InAt(1).AsRegister<Register>();
3580
Andra Danciu73c31802020-09-01 13:17:05 +00003581 const uint32_t coordtype0_offset = mirror::VarHandle::CoordinateType0Offset().Uint32Value();
3582 const uint32_t coordtype1_offset = mirror::VarHandle::CoordinateType1Offset().Uint32Value();
3583
3584 // Check that the VarHandle references an instance field by checking that
3585 // coordinateType1 == null. coordinateType0 should be not null, but this is handled by the
3586 // type compatibility check with the source object's type, which will fail for null.
3587 __ cmpl(Address(varhandle_object, coordtype1_offset), Immediate(0));
3588 __ j(kNotEqual, slow_path->GetEntryLabel());
3589
Andra Danciu63c0c2d2020-09-07 15:50:40 +00003590 // Check if the object is null
Ulyana Trafimovich98f01d12021-07-28 14:33:34 +00003591 if (!optimizations.GetSkipObjectNullCheck()) {
3592 __ testl(object, object);
3593 __ j(kZero, slow_path->GetEntryLabel());
3594 }
Andra Danciu63c0c2d2020-09-07 15:50:40 +00003595
Andra Danciu73c31802020-09-01 13:17:05 +00003596 // Check the object's class against coordinateType0.
3597 GenerateSubTypeObjectCheck(object,
3598 temp,
3599 Address(varhandle_object, coordtype0_offset),
3600 slow_path,
Andra Danciu63c0c2d2020-09-07 15:50:40 +00003601 assembler,
3602 /* object_can_be_null= */ false);
Andra Danciu73c31802020-09-01 13:17:05 +00003603}
3604
Andra Danciu9dfb1a92020-09-22 13:27:18 +00003605static void GenerateVarTypePrimitiveTypeCheck(Register varhandle_object,
3606 Register temp,
3607 DataType::Type type,
3608 SlowPathCode* slow_path,
3609 X86Assembler* assembler) {
3610 const uint32_t var_type_offset = mirror::VarHandle::VarTypeOffset().Uint32Value();
3611 const uint32_t primitive_type_offset = mirror::Class::PrimitiveTypeOffset().Uint32Value();
3612 const uint32_t primitive_type = static_cast<uint32_t>(DataTypeToPrimitive(type));
3613
3614 // We do not need a read barrier when loading a reference only for loading a constant field
3615 // through the reference.
3616 __ movl(temp, Address(varhandle_object, var_type_offset));
3617 __ MaybeUnpoisonHeapReference(temp);
3618 __ cmpw(Address(temp, primitive_type_offset), Immediate(primitive_type));
3619 __ j(kNotEqual, slow_path->GetEntryLabel());
3620}
3621
Andra Danciu73c31802020-09-01 13:17:05 +00003622static void GenerateVarHandleCommonChecks(HInvoke *invoke,
3623 Register temp,
3624 SlowPathCode* slow_path,
3625 X86Assembler* assembler) {
3626 LocationSummary* locations = invoke->GetLocations();
3627 Register vh_object = locations->InAt(0).AsRegister<Register>();
3628 mirror::VarHandle::AccessMode access_mode =
3629 mirror::VarHandle::GetAccessModeByIntrinsic(invoke->GetIntrinsic());
3630
3631 GenerateVarHandleAccessModeCheck(vh_object,
3632 access_mode,
3633 slow_path,
3634 assembler);
3635
Vladimir Markoa41ea272020-09-07 15:24:36 +00003636 size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke);
Andra Danciu73c31802020-09-01 13:17:05 +00003637 switch (expected_coordinates_count) {
3638 case 0u:
3639 GenerateVarHandleStaticFieldCheck(vh_object, slow_path, assembler);
3640 break;
3641 case 1u: {
Ulyana Trafimovich98f01d12021-07-28 14:33:34 +00003642 GenerateVarHandleInstanceFieldChecks(invoke, temp, slow_path, assembler);
Andra Danciu73c31802020-09-01 13:17:05 +00003643 break;
3644 }
3645 default:
3646 // Unimplemented
3647 UNREACHABLE();
3648 }
Andra Danciu73c31802020-09-01 13:17:05 +00003649
Andra Danciu9dfb1a92020-09-22 13:27:18 +00003650 // Check the return type and varType parameters.
3651 mirror::VarHandle::AccessModeTemplate access_mode_template =
3652 mirror::VarHandle::GetAccessModeTemplate(access_mode);
3653 DataType::Type type = invoke->GetType();
Andra Danciu73c31802020-09-01 13:17:05 +00003654
Andra Danciu9dfb1a92020-09-22 13:27:18 +00003655 switch (access_mode_template) {
3656 case mirror::VarHandle::AccessModeTemplate::kGet:
3657 // Check the varType.primitiveType against the type we're trying to retrieve. Reference types
3658 // are also checked later by a HCheckCast node as an additional check.
3659 GenerateVarTypePrimitiveTypeCheck(vh_object, temp, type, slow_path, assembler);
3660 break;
3661 case mirror::VarHandle::AccessModeTemplate::kSet:
3662 case mirror::VarHandle::AccessModeTemplate::kGetAndUpdate: {
3663 uint32_t value_index = invoke->GetNumberOfArguments() - 1;
3664 DataType::Type value_type = GetDataTypeFromShorty(invoke, value_index);
3665
3666 // Check the varType.primitiveType against the type of the value we're trying to set.
3667 GenerateVarTypePrimitiveTypeCheck(vh_object, temp, value_type, slow_path, assembler);
3668 if (value_type == DataType::Type::kReference) {
3669 const uint32_t var_type_offset = mirror::VarHandle::VarTypeOffset().Uint32Value();
3670
3671 // If the value type is a reference, check it against the varType.
3672 GenerateSubTypeObjectCheck(locations->InAt(value_index).AsRegister<Register>(),
3673 temp,
3674 Address(vh_object, var_type_offset),
3675 slow_path,
3676 assembler);
3677 }
3678 break;
3679 }
3680 case mirror::VarHandle::AccessModeTemplate::kCompareAndSet:
3681 case mirror::VarHandle::AccessModeTemplate::kCompareAndExchange: {
3682 uint32_t new_value_index = invoke->GetNumberOfArguments() - 1;
3683 uint32_t expected_value_index = invoke->GetNumberOfArguments() - 2;
3684 DataType::Type value_type = GetDataTypeFromShorty(invoke, new_value_index);
3685 DCHECK_EQ(value_type, GetDataTypeFromShorty(invoke, expected_value_index));
3686
3687 // Check the varType.primitiveType against the type of the expected value.
3688 GenerateVarTypePrimitiveTypeCheck(vh_object, temp, value_type, slow_path, assembler);
3689 if (value_type == DataType::Type::kReference) {
3690 const uint32_t var_type_offset = mirror::VarHandle::VarTypeOffset().Uint32Value();
3691
3692 // If the value type is a reference, check both the expected and the new value against
3693 // the varType.
3694 GenerateSubTypeObjectCheck(locations->InAt(new_value_index).AsRegister<Register>(),
3695 temp,
3696 Address(vh_object, var_type_offset),
3697 slow_path,
3698 assembler);
3699 GenerateSubTypeObjectCheck(locations->InAt(expected_value_index).AsRegister<Register>(),
3700 temp,
3701 Address(vh_object, var_type_offset),
3702 slow_path,
3703 assembler);
3704 }
3705 break;
3706 }
3707 }
Andra Danciu73c31802020-09-01 13:17:05 +00003708}
3709
3710// This method loads the field's address referred by a field VarHandle (base + offset).
3711// The return value is the register containing object's reference (in case of an instance field)
3712// or the declaring class (in case of a static field). The declaring class is stored in temp
3713// register. Field's offset is loaded to the `offset` register.
3714static Register GenerateVarHandleFieldReference(HInvoke* invoke,
3715 CodeGeneratorX86* codegen,
3716 Register temp,
3717 /*out*/ Register offset) {
3718 X86Assembler* assembler = codegen->GetAssembler();
3719 LocationSummary* locations = invoke->GetLocations();
3720 const uint32_t artfield_offset = mirror::FieldVarHandle::ArtFieldOffset().Uint32Value();
3721 const uint32_t offset_offset = ArtField::OffsetOffset().Uint32Value();
3722 const uint32_t declaring_class_offset = ArtField::DeclaringClassOffset().Uint32Value();
3723 Register varhandle_object = locations->InAt(0).AsRegister<Register>();
3724
3725 // Load the ArtField and the offset
3726 __ movl(temp, Address(varhandle_object, artfield_offset));
3727 __ movl(offset, Address(temp, offset_offset));
Vladimir Markoa41ea272020-09-07 15:24:36 +00003728 size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke);
Andra Danciu73c31802020-09-01 13:17:05 +00003729 if (expected_coordinates_count == 0) {
3730 // For static fields, load the declaring class
3731 InstructionCodeGeneratorX86* instr_codegen =
3732 down_cast<InstructionCodeGeneratorX86*>(codegen->GetInstructionVisitor());
3733 instr_codegen->GenerateGcRootFieldLoad(invoke,
3734 Location::RegisterLocation(temp),
3735 Address(temp, declaring_class_offset),
3736 /* fixup_label= */ nullptr,
3737 kCompilerReadBarrierOption);
3738 return temp;
3739 }
3740
Andra Danciu63c0c2d2020-09-07 15:50:40 +00003741 // For instance fields, return the register containing the object.
Andra Danciu73c31802020-09-01 13:17:05 +00003742 DCHECK_EQ(expected_coordinates_count, 1u);
Andra Danciu63c0c2d2020-09-07 15:50:40 +00003743
Andra Danciu73c31802020-09-01 13:17:05 +00003744 return locations->InAt(1).AsRegister<Register>();
3745}
3746
Andra Danciud0f71f22020-09-17 09:00:15 +00003747static void CreateVarHandleGetLocations(HInvoke* invoke) {
Andra Danciu1ca6f322020-08-12 08:58:07 +00003748 // The only read barrier implementation supporting the
3749 // VarHandleGet intrinsic is the Baker-style read barriers.
3750 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
3751 return;
Andra Danciue3e187f2020-07-30 12:19:31 +00003752 }
3753
Ulyana Trafimovich98f01d12021-07-28 14:33:34 +00003754 if (!HasVarHandleIntrinsicImplementation(invoke)) {
Andra Danciu0875b0a2020-08-28 11:49:44 +00003755 return;
3756 }
3757
3758 ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetAllocator();
3759 LocationSummary* locations = new (allocator) LocationSummary(
3760 invoke, LocationSummary::kCallOnSlowPath, kIntrinsified);
3761 locations->SetInAt(0, Location::RequiresRegister());
Vladimir Markoa41ea272020-09-07 15:24:36 +00003762 size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke);
Andra Danciu73c31802020-09-01 13:17:05 +00003763 if (expected_coordinates_count == 1u) {
Andra Danciu0875b0a2020-08-28 11:49:44 +00003764 // For instance fields, this is the source object.
3765 locations->SetInAt(1, Location::RequiresRegister());
3766 }
3767 locations->AddTemp(Location::RequiresRegister());
3768
Andra Danciu5e13d452020-09-08 14:35:09 +00003769 DataType::Type type = invoke->GetType();
Andra Danciu0875b0a2020-08-28 11:49:44 +00003770 switch (DataType::Kind(type)) {
3771 case DataType::Type::kInt64:
3772 locations->AddTemp(Location::RequiresRegister());
Andra Danciud0f71f22020-09-17 09:00:15 +00003773 if (invoke->GetIntrinsic() != Intrinsics::kVarHandleGet) {
3774 // We need an XmmRegister for Int64 to ensure an atomic load
3775 locations->AddTemp(Location::RequiresFpuRegister());
3776 }
Andra Danciu0875b0a2020-08-28 11:49:44 +00003777 FALLTHROUGH_INTENDED;
3778 case DataType::Type::kInt32:
3779 case DataType::Type::kReference:
3780 locations->SetOut(Location::RequiresRegister());
3781 break;
3782 default:
3783 DCHECK(DataType::IsFloatingPointType(type));
3784 locations->AddTemp(Location::RequiresRegister());
3785 locations->SetOut(Location::RequiresFpuRegister());
3786 }
Andra Danciue3e187f2020-07-30 12:19:31 +00003787}
3788
Andra Danciud0f71f22020-09-17 09:00:15 +00003789static void GenerateVarHandleGet(HInvoke* invoke, CodeGeneratorX86* codegen) {
Andra Danciu1ca6f322020-08-12 08:58:07 +00003790 // The only read barrier implementation supporting the
3791 // VarHandleGet intrinsic is the Baker-style read barriers.
3792 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
Andra Danciue74df4c2020-08-10 09:35:51 +00003793
Andra Danciud0f71f22020-09-17 09:00:15 +00003794 X86Assembler* assembler = codegen->GetAssembler();
Andra Danciu1ca6f322020-08-12 08:58:07 +00003795 LocationSummary* locations = invoke->GetLocations();
Andra Danciu1ca6f322020-08-12 08:58:07 +00003796 DataType::Type type = invoke->GetType();
Andra Danciu1ca6f322020-08-12 08:58:07 +00003797 DCHECK_NE(type, DataType::Type::kVoid);
Andra Danciu1ca6f322020-08-12 08:58:07 +00003798 Register temp = locations->GetTemp(0).AsRegister<Register>();
Andra Danciud0f71f22020-09-17 09:00:15 +00003799 SlowPathCode* slow_path = new (codegen->GetScopedAllocator()) IntrinsicSlowPathX86(invoke);
3800 codegen->AddSlowPath(slow_path);
Andra Danciu73c31802020-09-01 13:17:05 +00003801
3802 GenerateVarHandleCommonChecks(invoke, temp, slow_path, assembler);
Andra Danciue3e187f2020-07-30 12:19:31 +00003803
Andra Danciu1ca6f322020-08-12 08:58:07 +00003804 Location out = locations->Out();
3805 // Use 'out' as a temporary register if it's a core register
3806 Register offset =
3807 out.IsRegister() ? out.AsRegister<Register>() : locations->GetTemp(1).AsRegister<Register>();
Andra Danciu1ca6f322020-08-12 08:58:07 +00003808
Andra Danciu73c31802020-09-01 13:17:05 +00003809 // Get the field referred by the VarHandle. The returned register contains the object reference
3810 // or the declaring class. The field offset will be placed in 'offset'. For static fields, the
3811 // declaring class will be placed in 'temp' register.
Andra Danciud0f71f22020-09-17 09:00:15 +00003812 Register ref = GenerateVarHandleFieldReference(invoke, codegen, temp, offset);
3813 Address field_addr(ref, offset, TIMES_1, 0);
Andra Danciu1ca6f322020-08-12 08:58:07 +00003814
3815 // Load the value from the field
Andra Danciud0f71f22020-09-17 09:00:15 +00003816 if (type == DataType::Type::kReference && kCompilerReadBarrierOption == kWithReadBarrier) {
3817 codegen->GenerateReferenceLoadWithBakerReadBarrier(
3818 invoke, out, ref, field_addr, /* needs_null_check= */ false);
3819 } else if (type == DataType::Type::kInt64 &&
3820 invoke->GetIntrinsic() != Intrinsics::kVarHandleGet) {
3821 XmmRegister xmm_temp = locations->GetTemp(2).AsFpuRegister<XmmRegister>();
Ulya Trafimovich322eced2021-06-02 15:39:36 +01003822 codegen->LoadFromMemoryNoBarrier(
3823 type, out, field_addr, /* instr= */ nullptr, xmm_temp, /* is_atomic_load= */ true);
Andra Danciuaa358832020-08-25 15:09:43 +00003824 } else {
Andra Danciud0f71f22020-09-17 09:00:15 +00003825 codegen->LoadFromMemoryNoBarrier(type, out, field_addr);
3826 }
3827
3828 if (invoke->GetIntrinsic() == Intrinsics::kVarHandleGetVolatile ||
3829 invoke->GetIntrinsic() == Intrinsics::kVarHandleGetAcquire) {
3830 // Load fence to prevent load-load reordering.
3831 // Note that this is a no-op, thanks to the x86 memory model.
3832 codegen->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
Andra Danciu73c31802020-09-01 13:17:05 +00003833 }
3834
3835 __ Bind(slow_path->GetExitLabel());
3836}
3837
Andra Danciud0f71f22020-09-17 09:00:15 +00003838void IntrinsicLocationsBuilderX86::VisitVarHandleGet(HInvoke* invoke) {
3839 CreateVarHandleGetLocations(invoke);
3840}
3841
3842void IntrinsicCodeGeneratorX86::VisitVarHandleGet(HInvoke* invoke) {
3843 GenerateVarHandleGet(invoke, codegen_);
3844}
3845
3846void IntrinsicLocationsBuilderX86::VisitVarHandleGetVolatile(HInvoke* invoke) {
3847 CreateVarHandleGetLocations(invoke);
3848}
3849
3850void IntrinsicCodeGeneratorX86::VisitVarHandleGetVolatile(HInvoke* invoke) {
3851 GenerateVarHandleGet(invoke, codegen_);
3852}
3853
3854void IntrinsicLocationsBuilderX86::VisitVarHandleGetAcquire(HInvoke* invoke) {
3855 CreateVarHandleGetLocations(invoke);
3856}
3857
3858void IntrinsicCodeGeneratorX86::VisitVarHandleGetAcquire(HInvoke* invoke) {
3859 GenerateVarHandleGet(invoke, codegen_);
3860}
3861
3862void IntrinsicLocationsBuilderX86::VisitVarHandleGetOpaque(HInvoke* invoke) {
3863 CreateVarHandleGetLocations(invoke);
3864}
3865
3866void IntrinsicCodeGeneratorX86::VisitVarHandleGetOpaque(HInvoke* invoke) {
3867 GenerateVarHandleGet(invoke, codegen_);
3868}
3869
Andra Danciucde98192020-09-13 12:32:09 +00003870static void CreateVarHandleSetLocations(HInvoke* invoke) {
Andra Danciu73c31802020-09-01 13:17:05 +00003871 // The only read barrier implementation supporting the
3872 // VarHandleGet intrinsic is the Baker-style read barriers.
3873 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
3874 return;
3875 }
3876
Ulyana Trafimovich98f01d12021-07-28 14:33:34 +00003877 if (!HasVarHandleIntrinsicImplementation(invoke)) {
Andra Danciu73c31802020-09-01 13:17:05 +00003878 return;
3879 }
3880
3881 // The last argument should be the value we intend to set.
3882 uint32_t value_index = invoke->GetNumberOfArguments() - 1;
3883 HInstruction* value = invoke->InputAt(value_index);
3884 DataType::Type value_type = GetDataTypeFromShorty(invoke, value_index);
Andra Danciu71b26b22020-09-20 09:01:38 +00003885 bool needs_atomicity = invoke->GetIntrinsic() != Intrinsics::kVarHandleSet;
3886 if (value_type == DataType::Type::kInt64 && (!value->IsConstant() || needs_atomicity)) {
Andra Danciucde98192020-09-13 12:32:09 +00003887 // We avoid the case of a non-constant (or volatile) Int64 value because we would need to
3888 // place it in a register pair. If the slow path is taken, the ParallelMove might fail to move
3889 // the pair according to the X86DexCallingConvention in case of an overlap (e.g., move the
3890 // int64 value from <EAX, EBX> to <EBX, ECX>). (Bug: b/168687887)
Andra Danciu73c31802020-09-01 13:17:05 +00003891 return;
3892 }
3893
3894 ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetAllocator();
3895 LocationSummary* locations = new (allocator) LocationSummary(
3896 invoke, LocationSummary::kCallOnSlowPath, kIntrinsified);
Andra Danciu73c31802020-09-01 13:17:05 +00003897 locations->SetInAt(0, Location::RequiresRegister());
Vladimir Markoa41ea272020-09-07 15:24:36 +00003898 size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke);
Andra Danciu73c31802020-09-01 13:17:05 +00003899 if (expected_coordinates_count == 1u) {
3900 // For instance fields, this is the source object
3901 locations->SetInAt(1, Location::RequiresRegister());
Andra Danciu73c31802020-09-01 13:17:05 +00003902 }
3903
3904 switch (value_type) {
3905 case DataType::Type::kBool:
3906 case DataType::Type::kInt8:
3907 case DataType::Type::kUint8:
3908 // Ensure the value is in a byte register
3909 locations->SetInAt(value_index, Location::ByteRegisterOrConstant(EBX, value));
3910 break;
3911 case DataType::Type::kInt16:
3912 case DataType::Type::kUint16:
3913 case DataType::Type::kInt32:
3914 locations->SetInAt(value_index, Location::RegisterOrConstant(value));
3915 break;
3916 case DataType::Type::kInt64:
Andra Danciu71b26b22020-09-20 09:01:38 +00003917 // We only handle constant non-atomic int64 values.
Andra Danciu73c31802020-09-01 13:17:05 +00003918 DCHECK(value->IsConstant());
3919 locations->SetInAt(value_index, Location::ConstantLocation(value->AsConstant()));
3920 break;
3921 case DataType::Type::kReference:
3922 locations->SetInAt(value_index, Location::RequiresRegister());
3923 break;
3924 default:
3925 DCHECK(DataType::IsFloatingPointType(value_type));
Andra Danciu71b26b22020-09-20 09:01:38 +00003926 if (needs_atomicity && value_type == DataType::Type::kFloat64) {
Andra Danciucde98192020-09-13 12:32:09 +00003927 locations->SetInAt(value_index, Location::RequiresFpuRegister());
3928 } else {
3929 locations->SetInAt(value_index, Location::FpuRegisterOrConstant(value));
3930 }
3931 }
3932
3933 locations->AddTemp(Location::RequiresRegister());
3934 // This temporary register is also used for card for MarkGCCard. Make sure it's a byte register
3935 locations->AddTemp(Location::RegisterLocation(EAX));
3936 if (expected_coordinates_count == 0 && value_type == DataType::Type::kReference) {
3937 // For static reference fields, we need another temporary for the declaring class. We set it
3938 // last because we want to make sure that the first 2 temps are reserved for HandleFieldSet.
3939 locations->AddTemp(Location::RequiresRegister());
Andra Danciu73c31802020-09-01 13:17:05 +00003940 }
3941}
3942
Andra Danciucde98192020-09-13 12:32:09 +00003943static void GenerateVarHandleSet(HInvoke* invoke, CodeGeneratorX86* codegen) {
Andra Danciu73c31802020-09-01 13:17:05 +00003944 // The only read barrier implementation supporting the
3945 // VarHandleGet intrinsic is the Baker-style read barriers.
3946 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
3947
Andra Danciucde98192020-09-13 12:32:09 +00003948 X86Assembler* assembler = codegen->GetAssembler();
Andra Danciu73c31802020-09-01 13:17:05 +00003949 LocationSummary* locations = invoke->GetLocations();
3950 // The value we want to set is the last argument
3951 uint32_t value_index = invoke->GetNumberOfArguments() - 1;
Andra Danciu73c31802020-09-01 13:17:05 +00003952 DataType::Type value_type = GetDataTypeFromShorty(invoke, value_index);
Andra Danciu73c31802020-09-01 13:17:05 +00003953 Register temp = locations->GetTemp(0).AsRegister<Register>();
3954 Register temp2 = locations->GetTemp(1).AsRegister<Register>();
Andra Danciucde98192020-09-13 12:32:09 +00003955 SlowPathCode* slow_path = new (codegen->GetScopedAllocator()) IntrinsicSlowPathX86(invoke);
3956 codegen->AddSlowPath(slow_path);
Andra Danciu73c31802020-09-01 13:17:05 +00003957
3958 GenerateVarHandleCommonChecks(invoke, temp, slow_path, assembler);
3959
Andra Danciu9dfb1a92020-09-22 13:27:18 +00003960 // For static reference fields, we need another temporary for the declaring class. But since
3961 // for instance fields the object is in a separate register, it is safe to use the first
3962 // temporary register for GenerateVarHandleFieldReference.
3963 size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke);
3964 if (value_type == DataType::Type::kReference && expected_coordinates_count == 0) {
3965 temp = locations->GetTemp(2).AsRegister<Register>();
Andra Danciu73c31802020-09-01 13:17:05 +00003966 }
3967
3968 Register offset = temp2;
3969 // Get the field referred by the VarHandle. The returned register contains the object reference
3970 // or the declaring class. The field offset will be placed in 'offset'. For static fields, the
3971 // declaring class will be placed in 'temp' register.
Andra Danciucde98192020-09-13 12:32:09 +00003972 Register reference = GenerateVarHandleFieldReference(invoke, codegen, temp, offset);
Andra Danciu73c31802020-09-01 13:17:05 +00003973
Andra Danciucde98192020-09-13 12:32:09 +00003974 bool is_volatile = false;
3975 switch (invoke->GetIntrinsic()) {
3976 case Intrinsics::kVarHandleSet:
3977 case Intrinsics::kVarHandleSetOpaque:
3978 // The only constraint for setOpaque is to ensure bitwise atomicity (atomically set 64 bit
3979 // values), but we don't treat Int64 values because we would need to place it in a register
3980 // pair. If the slow path is taken, the Parallel move might fail to move the register pair
3981 // in case of an overlap (e.g., move from <EAX, EBX> to <EBX, ECX>). (Bug: b/168687887)
3982 break;
Andra Danciu71b26b22020-09-20 09:01:38 +00003983 case Intrinsics::kVarHandleSetRelease:
3984 // setRelease needs to ensure atomicity too. See the above comment.
3985 codegen->GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
3986 break;
Andra Danciucde98192020-09-13 12:32:09 +00003987 case Intrinsics::kVarHandleSetVolatile:
3988 is_volatile = true;
3989 break;
Andra Danciucde98192020-09-13 12:32:09 +00003990 default:
3991 LOG(FATAL) << "GenerateVarHandleSet received non-set intrinsic " << invoke->GetIntrinsic();
Andra Danciuaa358832020-08-25 15:09:43 +00003992 }
Andra Danciue3e187f2020-07-30 12:19:31 +00003993
Andra Danciucde98192020-09-13 12:32:09 +00003994 InstructionCodeGeneratorX86* instr_codegen =
3995 down_cast<InstructionCodeGeneratorX86*>(codegen->GetInstructionVisitor());
3996 // Store the value to the field
3997 instr_codegen->HandleFieldSet(invoke,
3998 value_index,
3999 value_type,
4000 Address(reference, offset, TIMES_1, 0),
4001 reference,
4002 is_volatile,
4003 /* value_can_be_null */ true);
4004
Andra Danciue3e187f2020-07-30 12:19:31 +00004005 __ Bind(slow_path->GetExitLabel());
4006}
4007
Andra Danciucde98192020-09-13 12:32:09 +00004008void IntrinsicLocationsBuilderX86::VisitVarHandleSet(HInvoke* invoke) {
4009 CreateVarHandleSetLocations(invoke);
4010}
4011
4012void IntrinsicCodeGeneratorX86::VisitVarHandleSet(HInvoke* invoke) {
4013 GenerateVarHandleSet(invoke, codegen_);
4014}
4015
4016void IntrinsicLocationsBuilderX86::VisitVarHandleSetVolatile(HInvoke* invoke) {
4017 CreateVarHandleSetLocations(invoke);
4018}
4019
4020void IntrinsicCodeGeneratorX86::VisitVarHandleSetVolatile(HInvoke* invoke) {
4021 GenerateVarHandleSet(invoke, codegen_);
4022}
4023
4024void IntrinsicLocationsBuilderX86::VisitVarHandleSetRelease(HInvoke* invoke) {
4025 CreateVarHandleSetLocations(invoke);
4026}
4027
4028void IntrinsicCodeGeneratorX86::VisitVarHandleSetRelease(HInvoke* invoke) {
4029 GenerateVarHandleSet(invoke, codegen_);
4030}
4031
4032void IntrinsicLocationsBuilderX86::VisitVarHandleSetOpaque(HInvoke* invoke) {
4033 CreateVarHandleSetLocations(invoke);
4034}
4035
4036void IntrinsicCodeGeneratorX86::VisitVarHandleSetOpaque(HInvoke* invoke) {
4037 GenerateVarHandleSet(invoke, codegen_);
4038}
4039
Andra Danciu9dfb1a92020-09-22 13:27:18 +00004040static void CreateVarHandleGetAndSetLocations(HInvoke* invoke) {
4041 // The only read barrier implementation supporting the
4042 // VarHandleGet intrinsic is the Baker-style read barriers.
4043 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
4044 return;
4045 }
4046
Ulyana Trafimovich98f01d12021-07-28 14:33:34 +00004047 if (!HasVarHandleIntrinsicImplementation(invoke)) {
Andra Danciu9dfb1a92020-09-22 13:27:18 +00004048 return;
4049 }
4050
4051 uint32_t number_of_arguments = invoke->GetNumberOfArguments();
4052 uint32_t value_index = number_of_arguments - 1;
4053 DataType::Type value_type = GetDataTypeFromShorty(invoke, value_index);
4054
4055 if (DataType::Is64BitType(value_type)) {
4056 // We avoid the case of an Int64/Float64 value because we would need to place it in a register
4057 // pair. If the slow path is taken, the ParallelMove might fail to move the pair according to
4058 // the X86DexCallingConvention in case of an overlap (e.g., move the 64 bit value from
4059 // <EAX, EBX> to <EBX, ECX>).
4060 return;
4061 }
4062
4063 ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetAllocator();
4064 LocationSummary* locations = new (allocator) LocationSummary(
4065 invoke, LocationSummary::kCallOnSlowPath, kIntrinsified);
4066 locations->AddTemp(Location::RequiresRegister());
4067 locations->AddTemp(Location::RequiresRegister());
4068 // We use this temporary for the card, so we need a byte register
4069 locations->AddTemp(Location::RegisterLocation(EBX));
4070 locations->SetInAt(0, Location::RequiresRegister());
4071 if (GetExpectedVarHandleCoordinatesCount(invoke) == 1u) {
4072 // For instance fields, this is the source object
4073 locations->SetInAt(1, Location::RequiresRegister());
4074 } else {
4075 // For static fields, we need another temp because one will be busy with the declaring class.
4076 locations->AddTemp(Location::RequiresRegister());
4077 }
4078 if (value_type == DataType::Type::kFloat32) {
4079 locations->AddTemp(Location::RegisterLocation(EAX));
4080 locations->SetInAt(value_index, Location::FpuRegisterOrConstant(invoke->InputAt(value_index)));
4081 locations->SetOut(Location::RequiresFpuRegister());
4082 } else {
4083 locations->SetInAt(value_index, Location::RegisterLocation(EAX));
4084 locations->SetOut(Location::RegisterLocation(EAX));
4085 }
4086}
4087
4088static void GenerateVarHandleGetAndSet(HInvoke* invoke, CodeGeneratorX86* codegen) {
4089 // The only read barrier implementation supporting the
4090 // VarHandleGet intrinsic is the Baker-style read barriers.
4091 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
4092
4093 X86Assembler* assembler = codegen->GetAssembler();
4094 LocationSummary* locations = invoke->GetLocations();
4095 // The value we want to set is the last argument
4096 uint32_t value_index = invoke->GetNumberOfArguments() - 1;
4097 Location value = locations->InAt(value_index);
4098 DataType::Type value_type = GetDataTypeFromShorty(invoke, value_index);
4099 Register temp = locations->GetTemp(1).AsRegister<Register>();
4100 Register temp2 = locations->GetTemp(2).AsRegister<Register>();
4101 SlowPathCode* slow_path = new (codegen->GetScopedAllocator()) IntrinsicSlowPathX86(invoke);
4102 codegen->AddSlowPath(slow_path);
4103
4104 GenerateVarHandleCommonChecks(invoke, temp, slow_path, assembler);
4105
4106 Register offset = locations->GetTemp(0).AsRegister<Register>();
4107 // Get the field referred by the VarHandle. The returned register contains the object reference
4108 // or the declaring class. The field offset will be placed in 'offset'. For static fields, the
4109 // declaring class will be placed in 'temp' register.
4110 Register reference = GenerateVarHandleFieldReference(invoke, codegen, temp, offset);
4111 Address field_addr(reference, offset, TIMES_1, 0);
4112
4113 if (invoke->GetIntrinsic() == Intrinsics::kVarHandleGetAndSetRelease) {
4114 codegen->GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
4115 }
4116
4117 size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke);
4118 // For static fields, we need another temporary for the declaring class. But since for instance
4119 // fields the object is in a separate register, it is safe to use the first temporary register.
4120 temp = expected_coordinates_count == 1u ? temp : locations->GetTemp(3).AsRegister<Register>();
4121 // No need for a lock prefix. `xchg` has an implicit lock when it is used with an address.
4122 switch (value_type) {
4123 case DataType::Type::kBool:
4124 __ xchgb(value.AsRegister<ByteRegister>(), field_addr);
4125 __ movzxb(locations->Out().AsRegister<Register>(),
4126 locations->Out().AsRegister<ByteRegister>());
4127 break;
4128 case DataType::Type::kInt8:
4129 __ xchgb(value.AsRegister<ByteRegister>(), field_addr);
4130 __ movsxb(locations->Out().AsRegister<Register>(),
4131 locations->Out().AsRegister<ByteRegister>());
4132 break;
4133 case DataType::Type::kUint16:
4134 __ xchgw(value.AsRegister<Register>(), field_addr);
4135 __ movzxw(locations->Out().AsRegister<Register>(), locations->Out().AsRegister<Register>());
4136 break;
4137 case DataType::Type::kInt16:
4138 __ xchgw(value.AsRegister<Register>(), field_addr);
4139 __ movsxw(locations->Out().AsRegister<Register>(), locations->Out().AsRegister<Register>());
4140 break;
4141 case DataType::Type::kInt32:
4142 __ xchgl(value.AsRegister<Register>(), field_addr);
4143 break;
4144 case DataType::Type::kFloat32:
4145 codegen->Move32(Location::RegisterLocation(EAX), value);
4146 __ xchgl(EAX, field_addr);
4147 __ movd(locations->Out().AsFpuRegister<XmmRegister>(), EAX);
4148 break;
4149 case DataType::Type::kReference: {
4150 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
4151 // Need to make sure the reference stored in the field is a to-space
4152 // one before attempting the CAS or the CAS could fail incorrectly.
4153 codegen->GenerateReferenceLoadWithBakerReadBarrier(
4154 invoke,
4155 // Unused, used only as a "temporary" within the read barrier.
4156 Location::RegisterLocation(temp),
4157 reference,
4158 field_addr,
4159 /* needs_null_check= */ false,
4160 /* always_update_field= */ true,
4161 &temp2);
4162 }
4163 codegen->MarkGCCard(
4164 temp, temp2, reference, value.AsRegister<Register>(), /* value_can_be_null= */ false);
4165 if (kPoisonHeapReferences) {
4166 __ movl(temp, value.AsRegister<Register>());
4167 __ PoisonHeapReference(temp);
4168 __ xchgl(temp, field_addr);
4169 __ UnpoisonHeapReference(temp);
4170 __ movl(locations->Out().AsRegister<Register>(), temp);
4171 } else {
4172 __ xchgl(locations->Out().AsRegister<Register>(), field_addr);
4173 }
4174 break;
4175 }
4176 default:
4177 UNREACHABLE();
4178 }
4179
4180 if (invoke->GetIntrinsic() == Intrinsics::kVarHandleGetAndSetAcquire) {
4181 codegen->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
4182 }
4183
4184 __ Bind(slow_path->GetExitLabel());
4185}
4186
4187void IntrinsicLocationsBuilderX86::VisitVarHandleGetAndSet(HInvoke* invoke) {
4188 CreateVarHandleGetAndSetLocations(invoke);
4189}
4190
4191void IntrinsicCodeGeneratorX86::VisitVarHandleGetAndSet(HInvoke* invoke) {
4192 GenerateVarHandleGetAndSet(invoke, codegen_);
4193}
4194
4195void IntrinsicLocationsBuilderX86::VisitVarHandleGetAndSetAcquire(HInvoke* invoke) {
4196 CreateVarHandleGetAndSetLocations(invoke);
4197}
4198
4199void IntrinsicCodeGeneratorX86::VisitVarHandleGetAndSetAcquire(HInvoke* invoke) {
4200 GenerateVarHandleGetAndSet(invoke, codegen_);
4201}
4202
4203void IntrinsicLocationsBuilderX86::VisitVarHandleGetAndSetRelease(HInvoke* invoke) {
4204 CreateVarHandleGetAndSetLocations(invoke);
4205}
4206
4207void IntrinsicCodeGeneratorX86::VisitVarHandleGetAndSetRelease(HInvoke* invoke) {
4208 GenerateVarHandleGetAndSet(invoke, codegen_);
4209}
4210
Andra Danciu370948e2020-09-23 08:07:25 +00004211static void CreateVarHandleCompareAndSetOrExchangeLocations(HInvoke* invoke) {
Andra Danciu5e13d452020-09-08 14:35:09 +00004212 // The only read barrier implementation supporting the
4213 // VarHandleGet intrinsic is the Baker-style read barriers.
4214 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
4215 return;
4216 }
4217
Ulyana Trafimovich98f01d12021-07-28 14:33:34 +00004218 if (!HasVarHandleIntrinsicImplementation(invoke)) {
Andra Danciu5e13d452020-09-08 14:35:09 +00004219 return;
4220 }
4221
4222 uint32_t number_of_arguments = invoke->GetNumberOfArguments();
4223 uint32_t expected_value_index = number_of_arguments - 2;
4224 uint32_t new_value_index = number_of_arguments - 1;
Andra Danciu370948e2020-09-23 08:07:25 +00004225 DataType::Type value_type = GetDataTypeFromShorty(invoke, expected_value_index);
4226 DCHECK_EQ(value_type, GetDataTypeFromShorty(invoke, new_value_index));
Andra Danciu5e13d452020-09-08 14:35:09 +00004227
Andra Danciu370948e2020-09-23 08:07:25 +00004228 if (DataType::Is64BitType(value_type)) {
Andra Danciu5e13d452020-09-08 14:35:09 +00004229 // We avoid the case of an Int64/Float64 value because we would need to place it in a register
4230 // pair. If the slow path is taken, the ParallelMove might fail to move the pair according to
4231 // the X86DexCallingConvention in case of an overlap (e.g., move the 64 bit value from
4232 // <EAX, EBX> to <EBX, ECX>).
4233 return;
4234 }
4235
Andra Danciu52d2c0c2020-09-15 14:27:21 +00004236 ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetAllocator();
4237 LocationSummary* locations = new (allocator) LocationSummary(
Andra Danciu5e13d452020-09-08 14:35:09 +00004238 invoke, LocationSummary::kCallOnSlowPath, kIntrinsified);
Andra Danciu5e13d452020-09-08 14:35:09 +00004239 locations->AddTemp(Location::RequiresRegister());
Andra Danciu8d8380a2020-09-11 09:24:01 +00004240 locations->AddTemp(Location::RequiresRegister());
4241 // We use this temporary for the card, so we need a byte register
4242 locations->AddTemp(Location::RegisterLocation(EBX));
Andra Danciu5e13d452020-09-08 14:35:09 +00004243 locations->SetInAt(0, Location::RequiresRegister());
4244 if (GetExpectedVarHandleCoordinatesCount(invoke) == 1u) {
4245 // For instance fields, this is the source object
4246 locations->SetInAt(1, Location::RequiresRegister());
Andra Danciu8d8380a2020-09-11 09:24:01 +00004247 } else {
4248 // For static fields, we need another temp because one will be busy with the declaring class.
Andra Danciu5e13d452020-09-08 14:35:09 +00004249 locations->AddTemp(Location::RequiresRegister());
4250 }
Andra Danciu370948e2020-09-23 08:07:25 +00004251 if (DataType::IsFloatingPointType(value_type)) {
Andra Danciu8d8380a2020-09-11 09:24:01 +00004252 // We need EAX for placing the expected value
4253 locations->AddTemp(Location::RegisterLocation(EAX));
4254 locations->SetInAt(new_value_index,
4255 Location::FpuRegisterOrConstant(invoke->InputAt(new_value_index)));
4256 locations->SetInAt(expected_value_index,
4257 Location::FpuRegisterOrConstant(invoke->InputAt(expected_value_index)));
Andra Danciu5e13d452020-09-08 14:35:09 +00004258 } else {
Andra Danciu8d8380a2020-09-11 09:24:01 +00004259 // Ensure it's in a byte register
4260 locations->SetInAt(new_value_index, Location::RegisterLocation(ECX));
4261 locations->SetInAt(expected_value_index, Location::RegisterLocation(EAX));
Andra Danciu5e13d452020-09-08 14:35:09 +00004262 }
4263
Andra Danciu370948e2020-09-23 08:07:25 +00004264 mirror::VarHandle::AccessModeTemplate access_mode_template =
4265 mirror::VarHandle::GetAccessModeTemplateByIntrinsic(invoke->GetIntrinsic());
4266
4267 if (access_mode_template == mirror::VarHandle::AccessModeTemplate::kCompareAndExchange &&
4268 value_type == DataType::Type::kFloat32) {
4269 locations->SetOut(Location::RequiresFpuRegister());
4270 } else {
4271 locations->SetOut(Location::RegisterLocation(EAX));
4272 }
Andra Danciu5e13d452020-09-08 14:35:09 +00004273}
4274
Andra Danciu370948e2020-09-23 08:07:25 +00004275static void GenerateVarHandleCompareAndSetOrExchange(HInvoke* invoke, CodeGeneratorX86* codegen) {
Andra Danciu5e13d452020-09-08 14:35:09 +00004276 // The only read barrier implementation supporting the
4277 // VarHandleGet intrinsic is the Baker-style read barriers.
4278 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
4279
Andra Danciu52d2c0c2020-09-15 14:27:21 +00004280 X86Assembler* assembler = codegen->GetAssembler();
Andra Danciu5e13d452020-09-08 14:35:09 +00004281 LocationSummary* locations = invoke->GetLocations();
4282 uint32_t number_of_arguments = invoke->GetNumberOfArguments();
4283 uint32_t expected_value_index = number_of_arguments - 2;
4284 uint32_t new_value_index = number_of_arguments - 1;
Andra Danciu8d8380a2020-09-11 09:24:01 +00004285 DataType::Type type = GetDataTypeFromShorty(invoke, expected_value_index);
4286 DCHECK_EQ(type, GetDataTypeFromShorty(invoke, new_value_index));
4287 Location expected_value = locations->InAt(expected_value_index);
4288 Location new_value = locations->InAt(new_value_index);
Andra Danciu8d8380a2020-09-11 09:24:01 +00004289 Register offset = locations->GetTemp(0).AsRegister<Register>();
Andra Danciu5e13d452020-09-08 14:35:09 +00004290 Register temp = locations->GetTemp(1).AsRegister<Register>();
Andra Danciu8d8380a2020-09-11 09:24:01 +00004291 Register temp2 = locations->GetTemp(2).AsRegister<Register>();
Andra Danciu52d2c0c2020-09-15 14:27:21 +00004292 SlowPathCode* slow_path = new (codegen->GetScopedAllocator()) IntrinsicSlowPathX86(invoke);
4293 codegen->AddSlowPath(slow_path);
Andra Danciu5e13d452020-09-08 14:35:09 +00004294
4295 GenerateVarHandleCommonChecks(invoke, temp, slow_path, assembler);
Andra Danciu5e13d452020-09-08 14:35:09 +00004296
Andra Danciu5e13d452020-09-08 14:35:09 +00004297 // Get the field referred by the VarHandle. The returned register contains the object reference
4298 // or the declaring class. The field offset will be placed in 'offset'. For static fields, the
4299 // declaring class will be placed in 'temp' register.
Andra Danciu52d2c0c2020-09-15 14:27:21 +00004300 Register reference = GenerateVarHandleFieldReference(invoke, codegen, temp, offset);
Andra Danciu5e13d452020-09-08 14:35:09 +00004301
Andra Danciu8d8380a2020-09-11 09:24:01 +00004302 uint32_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke);
4303 // For generating the compare and exchange, we need 2 temporaries. In case of a static field, the
4304 // first temporary contains the declaring class so we need another temporary. In case of an
4305 // instance field, the object comes in a separate register so it's safe to use the first temp.
4306 temp = (expected_coordinates_count == 1u) ? temp : locations->GetTemp(3).AsRegister<Register>();
4307 DCHECK_NE(temp, reference);
4308
Andra Danciu52d2c0c2020-09-15 14:27:21 +00004309 // We are using `lock cmpxchg` in all cases because there is no CAS equivalent that has weak
4310 // failure semantics. `lock cmpxchg` has full barrier semantics, and we don't need scheduling
4311 // barriers at this time.
4312
Andra Danciu370948e2020-09-23 08:07:25 +00004313 mirror::VarHandle::AccessModeTemplate access_mode_template =
4314 mirror::VarHandle::GetAccessModeTemplateByIntrinsic(invoke->GetIntrinsic());
4315 bool is_cmpxchg =
4316 access_mode_template == mirror::VarHandle::AccessModeTemplate::kCompareAndExchange;
4317
Andra Danciu8d8380a2020-09-11 09:24:01 +00004318 if (type == DataType::Type::kReference) {
Andra Danciu370948e2020-09-23 08:07:25 +00004319 GenReferenceCAS(
4320 invoke, codegen, expected_value, new_value, reference, offset, temp, temp2, is_cmpxchg);
Andra Danciu8d8380a2020-09-11 09:24:01 +00004321 } else {
4322 Location out = locations->Out();
Andra Danciu370948e2020-09-23 08:07:25 +00004323 GenPrimitiveCAS(
4324 type, codegen, expected_value, new_value, reference, offset, out, temp, is_cmpxchg);
Andra Danciu5e13d452020-09-08 14:35:09 +00004325 }
4326
Andra Danciu5e13d452020-09-08 14:35:09 +00004327 __ Bind(slow_path->GetExitLabel());
4328}
Andra Danciue3e187f2020-07-30 12:19:31 +00004329
Andra Danciu52d2c0c2020-09-15 14:27:21 +00004330void IntrinsicLocationsBuilderX86::VisitVarHandleCompareAndSet(HInvoke* invoke) {
Andra Danciu370948e2020-09-23 08:07:25 +00004331 CreateVarHandleCompareAndSetOrExchangeLocations(invoke);
Andra Danciu52d2c0c2020-09-15 14:27:21 +00004332}
4333
4334void IntrinsicCodeGeneratorX86::VisitVarHandleCompareAndSet(HInvoke* invoke) {
Andra Danciu370948e2020-09-23 08:07:25 +00004335 GenerateVarHandleCompareAndSetOrExchange(invoke, codegen_);
Andra Danciu52d2c0c2020-09-15 14:27:21 +00004336}
4337
4338void IntrinsicLocationsBuilderX86::VisitVarHandleWeakCompareAndSet(HInvoke* invoke) {
Andra Danciu370948e2020-09-23 08:07:25 +00004339 CreateVarHandleCompareAndSetOrExchangeLocations(invoke);
Andra Danciu52d2c0c2020-09-15 14:27:21 +00004340}
4341
4342void IntrinsicCodeGeneratorX86::VisitVarHandleWeakCompareAndSet(HInvoke* invoke) {
Andra Danciu370948e2020-09-23 08:07:25 +00004343 GenerateVarHandleCompareAndSetOrExchange(invoke, codegen_);
Andra Danciu52d2c0c2020-09-15 14:27:21 +00004344}
4345
4346void IntrinsicLocationsBuilderX86::VisitVarHandleWeakCompareAndSetPlain(HInvoke* invoke) {
Andra Danciu370948e2020-09-23 08:07:25 +00004347 CreateVarHandleCompareAndSetOrExchangeLocations(invoke);
Andra Danciu52d2c0c2020-09-15 14:27:21 +00004348}
4349
4350void IntrinsicCodeGeneratorX86::VisitVarHandleWeakCompareAndSetPlain(HInvoke* invoke) {
Andra Danciu370948e2020-09-23 08:07:25 +00004351 GenerateVarHandleCompareAndSetOrExchange(invoke, codegen_);
Andra Danciu52d2c0c2020-09-15 14:27:21 +00004352}
4353
4354void IntrinsicLocationsBuilderX86::VisitVarHandleWeakCompareAndSetAcquire(HInvoke* invoke) {
Andra Danciu370948e2020-09-23 08:07:25 +00004355 CreateVarHandleCompareAndSetOrExchangeLocations(invoke);
Andra Danciu52d2c0c2020-09-15 14:27:21 +00004356}
4357
4358void IntrinsicCodeGeneratorX86::VisitVarHandleWeakCompareAndSetAcquire(HInvoke* invoke) {
Andra Danciu370948e2020-09-23 08:07:25 +00004359 GenerateVarHandleCompareAndSetOrExchange(invoke, codegen_);
Andra Danciu52d2c0c2020-09-15 14:27:21 +00004360}
4361
4362void IntrinsicLocationsBuilderX86::VisitVarHandleWeakCompareAndSetRelease(HInvoke* invoke) {
Andra Danciu370948e2020-09-23 08:07:25 +00004363 CreateVarHandleCompareAndSetOrExchangeLocations(invoke);
Andra Danciu52d2c0c2020-09-15 14:27:21 +00004364}
4365
4366void IntrinsicCodeGeneratorX86::VisitVarHandleWeakCompareAndSetRelease(HInvoke* invoke) {
Andra Danciu370948e2020-09-23 08:07:25 +00004367 GenerateVarHandleCompareAndSetOrExchange(invoke, codegen_);
4368}
4369
4370void IntrinsicLocationsBuilderX86::VisitVarHandleCompareAndExchange(HInvoke* invoke) {
4371 CreateVarHandleCompareAndSetOrExchangeLocations(invoke);
4372}
4373
4374void IntrinsicCodeGeneratorX86::VisitVarHandleCompareAndExchange(HInvoke* invoke) {
4375 GenerateVarHandleCompareAndSetOrExchange(invoke, codegen_);
4376}
4377
4378void IntrinsicLocationsBuilderX86::VisitVarHandleCompareAndExchangeAcquire(HInvoke* invoke) {
4379 CreateVarHandleCompareAndSetOrExchangeLocations(invoke);
4380}
4381
4382void IntrinsicCodeGeneratorX86::VisitVarHandleCompareAndExchangeAcquire(HInvoke* invoke) {
4383 GenerateVarHandleCompareAndSetOrExchange(invoke, codegen_);
4384}
4385
4386void IntrinsicLocationsBuilderX86::VisitVarHandleCompareAndExchangeRelease(HInvoke* invoke) {
4387 CreateVarHandleCompareAndSetOrExchangeLocations(invoke);
4388}
4389
4390void IntrinsicCodeGeneratorX86::VisitVarHandleCompareAndExchangeRelease(HInvoke* invoke) {
4391 GenerateVarHandleCompareAndSetOrExchange(invoke, codegen_);
Andra Danciu52d2c0c2020-09-15 14:27:21 +00004392}
4393
Andra Danciu49cde242020-09-22 08:38:50 +00004394static void CreateVarHandleGetAndAddLocations(HInvoke* invoke) {
Andra Danciueb2c9dd2020-09-14 13:22:40 +00004395 // The only read barrier implementation supporting the
4396 // VarHandleGet intrinsic is the Baker-style read barriers.
4397 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
4398 return;
4399 }
4400
Ulyana Trafimovich98f01d12021-07-28 14:33:34 +00004401 if (!HasVarHandleIntrinsicImplementation(invoke)) {
Andra Danciueb2c9dd2020-09-14 13:22:40 +00004402 return;
4403 }
4404
4405 // The last argument should be the value we intend to set.
4406 uint32_t value_index = invoke->GetNumberOfArguments() - 1;
4407 DataType::Type value_type = GetDataTypeFromShorty(invoke, value_index);
4408 if (DataType::Is64BitType(value_type)) {
4409 // We avoid the case of an Int64/Float64 value because we would need to place it in a register
4410 // pair. If the slow path is taken, the ParallelMove might fail to move the pair according to
4411 // the X86DexCallingConvention in case of an overlap (e.g., move the 64 bit value from
4412 // <EAX, EBX> to <EBX, ECX>). (Bug: b/168687887)
4413 return;
4414 }
4415
4416 ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetAllocator();
4417 LocationSummary* locations = new (allocator) LocationSummary(
4418 invoke, LocationSummary::kCallOnSlowPath, kIntrinsified);
4419 locations->AddTemp(Location::RequiresRegister());
4420 locations->AddTemp(Location::RequiresRegister());
4421 locations->SetInAt(0, Location::RequiresRegister());
4422 size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke);
4423 if (expected_coordinates_count == 1u) {
4424 // For instance fields, this is the source object
4425 locations->SetInAt(1, Location::RequiresRegister());
4426 } else {
4427 // For static fields, we need another temp because one will be busy with the declaring class.
4428 locations->AddTemp(Location::RequiresRegister());
4429 }
4430
4431 if (DataType::IsFloatingPointType(value_type)) {
4432 locations->AddTemp(Location::RequiresFpuRegister());
4433 locations->AddTemp(Location::RegisterLocation(EAX));
4434 locations->SetInAt(value_index, Location::RequiresFpuRegister());
4435 locations->SetOut(Location::RequiresFpuRegister());
4436 } else {
4437 // xadd updates the register argument with the old value. ByteRegister required for xaddb.
4438 locations->SetInAt(value_index, Location::RegisterLocation(EAX));
4439 locations->SetOut(Location::RegisterLocation(EAX));
4440 }
4441}
4442
Andra Danciu49cde242020-09-22 08:38:50 +00004443static void GenerateVarHandleGetAndAdd(HInvoke* invoke, CodeGeneratorX86* codegen) {
Andra Danciueb2c9dd2020-09-14 13:22:40 +00004444 // The only read barrier implementation supporting the
4445 // VarHandleGet intrinsic is the Baker-style read barriers.
4446 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
4447
Andra Danciueb2c9dd2020-09-14 13:22:40 +00004448 X86Assembler* assembler = codegen->GetAssembler();
4449 LocationSummary* locations = invoke->GetLocations();
4450 uint32_t number_of_arguments = invoke->GetNumberOfArguments();
4451 uint32_t value_index = number_of_arguments - 1;
4452 DataType::Type type = GetDataTypeFromShorty(invoke, value_index);
4453 DCHECK_EQ(type, invoke->GetType());
4454 Location value_loc = locations->InAt(value_index);
Andra Danciueb2c9dd2020-09-14 13:22:40 +00004455 Register temp = locations->GetTemp(0).AsRegister<Register>();
4456 SlowPathCode* slow_path = new (codegen->GetScopedAllocator()) IntrinsicSlowPathX86(invoke);
4457 codegen->AddSlowPath(slow_path);
4458
4459 GenerateVarHandleCommonChecks(invoke, temp, slow_path, assembler);
4460
Andra Danciueb2c9dd2020-09-14 13:22:40 +00004461 Register offset = locations->GetTemp(1).AsRegister<Register>();
4462 // Get the field referred by the VarHandle. The returned register contains the object reference
4463 // or the declaring class. The field offset will be placed in 'offset'. For static fields, the
4464 // declaring class will be placed in 'temp' register.
4465 Register reference = GenerateVarHandleFieldReference(invoke, codegen, temp, offset);
4466
4467 size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke);
4468 temp = (expected_coordinates_count == 1u) ? temp : locations->GetTemp(2).AsRegister<Register>();
4469 DCHECK_NE(temp, reference);
4470 Address field_addr(reference, offset, TIMES_1, 0);
4471
4472 switch (type) {
4473 case DataType::Type::kInt8:
4474 __ LockXaddb(field_addr, value_loc.AsRegister<ByteRegister>());
4475 __ movsxb(locations->Out().AsRegister<Register>(),
4476 locations->Out().AsRegister<ByteRegister>());
4477 break;
4478 case DataType::Type::kInt16:
4479 __ LockXaddw(field_addr, value_loc.AsRegister<Register>());
4480 __ movsxw(locations->Out().AsRegister<Register>(), locations->Out().AsRegister<Register>());
4481 break;
4482 case DataType::Type::kUint16:
4483 __ LockXaddw(field_addr, value_loc.AsRegister<Register>());
4484 __ movzxw(locations->Out().AsRegister<Register>(), locations->Out().AsRegister<Register>());
4485 break;
4486 case DataType::Type::kInt32:
4487 __ LockXaddl(field_addr, value_loc.AsRegister<Register>());
4488 break;
4489 case DataType::Type::kFloat32: {
4490 Location temp_float =
4491 (expected_coordinates_count == 1u) ? locations->GetTemp(2) : locations->GetTemp(3);
4492 DCHECK(temp_float.IsFpuRegister());
4493 Location eax = Location::RegisterLocation(EAX);
4494 NearLabel try_again;
4495 __ Bind(&try_again);
Andra Danciud0f71f22020-09-17 09:00:15 +00004496 __ movss(temp_float.AsFpuRegister<XmmRegister>(), field_addr);
Andra Danciueb2c9dd2020-09-14 13:22:40 +00004497 __ movd(EAX, temp_float.AsFpuRegister<XmmRegister>());
4498 __ addss(temp_float.AsFpuRegister<XmmRegister>(),
Andra Danciud0f71f22020-09-17 09:00:15 +00004499 value_loc.AsFpuRegister<XmmRegister>());
Andra Danciueb2c9dd2020-09-14 13:22:40 +00004500 GenPrimitiveLockedCmpxchg(type,
4501 codegen,
Orion Hodsond6ea38b2020-09-18 13:57:23 +01004502 /* expected_value= */ eax,
Andra Danciueb2c9dd2020-09-14 13:22:40 +00004503 /* new_value= */ temp_float,
4504 reference,
4505 offset,
4506 temp);
4507 __ j(kNotZero, &try_again);
4508
4509 // The old value is present in EAX.
4510 codegen->Move32(locations->Out(), eax);
4511 break;
4512 }
4513 default:
4514 UNREACHABLE();
4515 }
4516
4517 __ Bind(slow_path->GetExitLabel());
4518}
4519
Andra Danciu49cde242020-09-22 08:38:50 +00004520void IntrinsicLocationsBuilderX86::VisitVarHandleGetAndAdd(HInvoke* invoke) {
4521 CreateVarHandleGetAndAddLocations(invoke);
4522}
4523
4524void IntrinsicCodeGeneratorX86::VisitVarHandleGetAndAdd(HInvoke* invoke) {
4525 GenerateVarHandleGetAndAdd(invoke, codegen_);
4526}
4527
4528void IntrinsicLocationsBuilderX86::VisitVarHandleGetAndAddAcquire(HInvoke* invoke) {
4529 CreateVarHandleGetAndAddLocations(invoke);
4530}
4531
4532void IntrinsicCodeGeneratorX86::VisitVarHandleGetAndAddAcquire(HInvoke* invoke) {
4533 GenerateVarHandleGetAndAdd(invoke, codegen_);
4534}
4535
4536void IntrinsicLocationsBuilderX86::VisitVarHandleGetAndAddRelease(HInvoke* invoke) {
4537 CreateVarHandleGetAndAddLocations(invoke);
4538}
4539
4540void IntrinsicCodeGeneratorX86::VisitVarHandleGetAndAddRelease(HInvoke* invoke) {
4541 GenerateVarHandleGetAndAdd(invoke, codegen_);
4542}
4543
Andra Danciu6edcc082020-09-18 09:21:26 +00004544static void CreateVarHandleGetAndBitwiseOpLocations(HInvoke* invoke) {
4545 // The only read barrier implementation supporting the
4546 // VarHandleGet intrinsic is the Baker-style read barriers.
4547 if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
4548 return;
4549 }
4550
Ulyana Trafimovich98f01d12021-07-28 14:33:34 +00004551 if (!HasVarHandleIntrinsicImplementation(invoke)) {
Andra Danciu6edcc082020-09-18 09:21:26 +00004552 return;
4553 }
4554
4555 // The last argument should be the value we intend to set.
4556 uint32_t value_index = invoke->GetNumberOfArguments() - 1;
4557 if (DataType::Is64BitType(GetDataTypeFromShorty(invoke, value_index))) {
4558 // We avoid the case of an Int64 value because we would need to place it in a register pair.
4559 // If the slow path is taken, the ParallelMove might fail to move the pair according to the
4560 // X86DexCallingConvention in case of an overlap (e.g., move the 64 bit value from
4561 // <EAX, EBX> to <EBX, ECX>). (Bug: b/168687887)
4562 return;
4563 }
4564
4565 ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetAllocator();
4566 LocationSummary* locations = new (allocator) LocationSummary(
4567 invoke, LocationSummary::kCallOnSlowPath, kIntrinsified);
4568 // We need a byte register temp to store the result of the bitwise operation
4569 locations->AddTemp(Location::RegisterLocation(EBX));
4570 locations->AddTemp(Location::RequiresRegister());
4571 locations->SetInAt(0, Location::RequiresRegister());
4572 size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke);
4573 if (expected_coordinates_count == 1u) {
4574 // For instance fields, this is the source object
4575 locations->SetInAt(1, Location::RequiresRegister());
4576 } else {
4577 // For static fields, we need another temp because one will be busy with the declaring class.
4578 locations->AddTemp(Location::RequiresRegister());
4579 }
4580
4581 locations->SetInAt(value_index, Location::RegisterOrConstant(invoke->InputAt(value_index)));
4582 locations->SetOut(Location::RegisterLocation(EAX));
4583}
4584
4585static void GenerateBitwiseOp(HInvoke* invoke,
4586 CodeGeneratorX86* codegen,
4587 Register left,
4588 Register right) {
4589 X86Assembler* assembler = codegen->GetAssembler();
4590
4591 switch (invoke->GetIntrinsic()) {
4592 case Intrinsics::kVarHandleGetAndBitwiseOr:
4593 case Intrinsics::kVarHandleGetAndBitwiseOrAcquire:
4594 case Intrinsics::kVarHandleGetAndBitwiseOrRelease:
4595 __ orl(left, right);
4596 break;
4597 case Intrinsics::kVarHandleGetAndBitwiseXor:
4598 case Intrinsics::kVarHandleGetAndBitwiseXorAcquire:
4599 case Intrinsics::kVarHandleGetAndBitwiseXorRelease:
4600 __ xorl(left, right);
4601 break;
4602 case Intrinsics::kVarHandleGetAndBitwiseAnd:
4603 case Intrinsics::kVarHandleGetAndBitwiseAndAcquire:
4604 case Intrinsics::kVarHandleGetAndBitwiseAndRelease:
4605 __ andl(left, right);
4606 break;
4607 default:
4608 UNREACHABLE();
4609 }
4610}
4611
4612static void GenerateVarHandleGetAndBitwiseOp(HInvoke* invoke, CodeGeneratorX86* codegen) {
4613 // The only read barrier implementation supporting the
4614 // VarHandleGet intrinsic is the Baker-style read barriers.
4615 DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
4616
4617 X86Assembler* assembler = codegen->GetAssembler();
4618 LocationSummary* locations = invoke->GetLocations();
4619 uint32_t value_index = invoke->GetNumberOfArguments() - 1;
4620 DataType::Type type = GetDataTypeFromShorty(invoke, value_index);
4621 DCHECK_EQ(type, invoke->GetType());
Andra Danciu6edcc082020-09-18 09:21:26 +00004622 Register temp = locations->GetTemp(0).AsRegister<Register>();
4623 SlowPathCode* slow_path = new (codegen->GetScopedAllocator()) IntrinsicSlowPathX86(invoke);
4624 codegen->AddSlowPath(slow_path);
4625
4626 GenerateVarHandleCommonChecks(invoke, temp, slow_path, assembler);
4627
Andra Danciu6edcc082020-09-18 09:21:26 +00004628 Register offset = locations->GetTemp(1).AsRegister<Register>();
4629 size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke);
4630 // For static field, we need another temporary because the first one contains the declaring class
4631 Register reference =
4632 (expected_coordinates_count == 1u) ? temp : locations->GetTemp(2).AsRegister<Register>();
4633 // Get the field referred by the VarHandle. The returned register contains the object reference
4634 // or the declaring class. The field offset will be placed in 'offset'. For static fields, the
4635 // declaring class will be placed in 'reference' register.
4636 reference = GenerateVarHandleFieldReference(invoke, codegen, reference, offset);
4637 DCHECK_NE(temp, reference);
4638 Address field_addr(reference, offset, TIMES_1, 0);
4639
4640 Register out = locations->Out().AsRegister<Register>();
4641 DCHECK_EQ(out, EAX);
4642
4643 if (invoke->GetIntrinsic() == Intrinsics::kVarHandleGetAndBitwiseOrRelease ||
4644 invoke->GetIntrinsic() == Intrinsics::kVarHandleGetAndBitwiseXorRelease ||
4645 invoke->GetIntrinsic() == Intrinsics::kVarHandleGetAndBitwiseAndRelease) {
4646 codegen->GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
4647 }
4648
4649 NearLabel try_again;
4650 __ Bind(&try_again);
4651 // Place the expected value in EAX for cmpxchg
Andra Danciud0f71f22020-09-17 09:00:15 +00004652 codegen->LoadFromMemoryNoBarrier(type, locations->Out(), field_addr);
Andra Danciu6edcc082020-09-18 09:21:26 +00004653 codegen->Move32(locations->GetTemp(0), locations->InAt(value_index));
4654 GenerateBitwiseOp(invoke, codegen, temp, out);
4655 GenPrimitiveLockedCmpxchg(type,
4656 codegen,
4657 /* expected_value= */ locations->Out(),
4658 /* new_value= */ locations->GetTemp(0),
4659 reference,
4660 offset);
4661 // If the cmpxchg failed, another thread changed the value so try again.
4662 __ j(kNotZero, &try_again);
4663
4664 // The old value is present in EAX.
4665
4666 if (invoke->GetIntrinsic() == Intrinsics::kVarHandleGetAndBitwiseOrAcquire ||
4667 invoke->GetIntrinsic() == Intrinsics::kVarHandleGetAndBitwiseXorAcquire ||
4668 invoke->GetIntrinsic() == Intrinsics::kVarHandleGetAndBitwiseAndAcquire) {
4669 codegen->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
4670 }
4671
4672 __ Bind(slow_path->GetExitLabel());
4673}
4674
4675void IntrinsicLocationsBuilderX86::VisitVarHandleGetAndBitwiseOr(HInvoke* invoke) {
4676 CreateVarHandleGetAndBitwiseOpLocations(invoke);
4677}
4678
4679void IntrinsicCodeGeneratorX86::VisitVarHandleGetAndBitwiseOr(HInvoke* invoke) {
4680 GenerateVarHandleGetAndBitwiseOp(invoke, codegen_);
4681}
4682
4683void IntrinsicLocationsBuilderX86::VisitVarHandleGetAndBitwiseOrAcquire(HInvoke* invoke) {
4684 CreateVarHandleGetAndBitwiseOpLocations(invoke);
4685}
4686
4687void IntrinsicCodeGeneratorX86::VisitVarHandleGetAndBitwiseOrAcquire(HInvoke* invoke) {
4688 GenerateVarHandleGetAndBitwiseOp(invoke, codegen_);
4689}
4690
4691void IntrinsicLocationsBuilderX86::VisitVarHandleGetAndBitwiseOrRelease(HInvoke* invoke) {
4692 CreateVarHandleGetAndBitwiseOpLocations(invoke);
4693}
4694
4695void IntrinsicCodeGeneratorX86::VisitVarHandleGetAndBitwiseOrRelease(HInvoke* invoke) {
4696 GenerateVarHandleGetAndBitwiseOp(invoke, codegen_);
4697}
4698
4699void IntrinsicLocationsBuilderX86::VisitVarHandleGetAndBitwiseXor(HInvoke* invoke) {
4700 CreateVarHandleGetAndBitwiseOpLocations(invoke);
4701}
4702
4703void IntrinsicCodeGeneratorX86::VisitVarHandleGetAndBitwiseXor(HInvoke* invoke) {
4704 GenerateVarHandleGetAndBitwiseOp(invoke, codegen_);
4705}
4706
4707void IntrinsicLocationsBuilderX86::VisitVarHandleGetAndBitwiseXorAcquire(HInvoke* invoke) {
4708 CreateVarHandleGetAndBitwiseOpLocations(invoke);
4709}
4710
4711void IntrinsicCodeGeneratorX86::VisitVarHandleGetAndBitwiseXorAcquire(HInvoke* invoke) {
4712 GenerateVarHandleGetAndBitwiseOp(invoke, codegen_);
4713}
4714
4715void IntrinsicLocationsBuilderX86::VisitVarHandleGetAndBitwiseXorRelease(HInvoke* invoke) {
4716 CreateVarHandleGetAndBitwiseOpLocations(invoke);
4717}
4718
4719void IntrinsicCodeGeneratorX86::VisitVarHandleGetAndBitwiseXorRelease(HInvoke* invoke) {
4720 GenerateVarHandleGetAndBitwiseOp(invoke, codegen_);
4721}
4722
4723void IntrinsicLocationsBuilderX86::VisitVarHandleGetAndBitwiseAnd(HInvoke* invoke) {
4724 CreateVarHandleGetAndBitwiseOpLocations(invoke);
4725}
4726
4727void IntrinsicCodeGeneratorX86::VisitVarHandleGetAndBitwiseAnd(HInvoke* invoke) {
4728 GenerateVarHandleGetAndBitwiseOp(invoke, codegen_);
4729}
4730
4731void IntrinsicLocationsBuilderX86::VisitVarHandleGetAndBitwiseAndAcquire(HInvoke* invoke) {
4732 CreateVarHandleGetAndBitwiseOpLocations(invoke);
4733}
4734
4735void IntrinsicCodeGeneratorX86::VisitVarHandleGetAndBitwiseAndAcquire(HInvoke* invoke) {
4736 GenerateVarHandleGetAndBitwiseOp(invoke, codegen_);
4737}
4738
4739void IntrinsicLocationsBuilderX86::VisitVarHandleGetAndBitwiseAndRelease(HInvoke* invoke) {
4740 CreateVarHandleGetAndBitwiseOpLocations(invoke);
4741}
4742
4743void IntrinsicCodeGeneratorX86::VisitVarHandleGetAndBitwiseAndRelease(HInvoke* invoke) {
4744 GenerateVarHandleGetAndBitwiseOp(invoke, codegen_);
4745}
Andra Danciueb2c9dd2020-09-14 13:22:40 +00004746
Shalini Salomi Bodapati6545ee32021-11-02 20:01:06 +05304747static void GenerateMathFma(HInvoke* invoke, CodeGeneratorX86* codegen) {
4748 DCHECK(DataType::IsFloatingPointType(invoke->GetType()));
4749 LocationSummary* locations = invoke->GetLocations();
4750 DCHECK(locations->InAt(0).Equals(locations->Out()));
4751 X86Assembler* assembler = codegen->GetAssembler();
4752 XmmRegister left = locations->InAt(0).AsFpuRegister<XmmRegister>();
4753 XmmRegister right = locations->InAt(1).AsFpuRegister<XmmRegister>();
4754 XmmRegister accumulator = locations->InAt(2).AsFpuRegister<XmmRegister>();
4755 if (invoke->GetType() == DataType::Type::kFloat32) {
4756 __ vfmadd213ss(left, right, accumulator);
4757 } else {
4758 DCHECK_EQ(invoke->GetType(), DataType::Type::kFloat64);
4759 __ vfmadd213sd(left, right, accumulator);
4760 }
4761}
4762
4763void IntrinsicCodeGeneratorX86::VisitMathFmaDouble(HInvoke* invoke) {
4764 DCHECK(codegen_->GetInstructionSetFeatures().HasAVX2());
4765 GenerateMathFma(invoke, codegen_);
4766}
4767
4768void IntrinsicLocationsBuilderX86::VisitMathFmaDouble(HInvoke* invoke) {
4769 if (codegen_->GetInstructionSetFeatures().HasAVX2()) {
4770 CreateFPFPFPToFPCallLocations(allocator_, invoke);
4771 }
4772}
4773
4774void IntrinsicCodeGeneratorX86::VisitMathFmaFloat(HInvoke* invoke) {
4775 DCHECK(codegen_->GetInstructionSetFeatures().HasAVX2());
4776 GenerateMathFma(invoke, codegen_);
4777}
4778
4779void IntrinsicLocationsBuilderX86::VisitMathFmaFloat(HInvoke* invoke) {
4780 if (codegen_->GetInstructionSetFeatures().HasAVX2()) {
4781 CreateFPFPFPToFPCallLocations(allocator_, invoke);
4782 }
4783}
4784
Aart Bik2f9fcc92016-03-01 15:16:54 -08004785UNIMPLEMENTED_INTRINSIC(X86, MathRoundDouble)
Aart Bik2f9fcc92016-03-01 15:16:54 -08004786UNIMPLEMENTED_INTRINSIC(X86, FloatIsInfinite)
4787UNIMPLEMENTED_INTRINSIC(X86, DoubleIsInfinite)
4788UNIMPLEMENTED_INTRINSIC(X86, IntegerHighestOneBit)
4789UNIMPLEMENTED_INTRINSIC(X86, LongHighestOneBit)
Artem Serova3bd4ec2020-08-27 16:26:17 +01004790UNIMPLEMENTED_INTRINSIC(X86, LongDivideUnsigned)
xueliang.zhongcb58b072017-10-13 12:06:56 +01004791UNIMPLEMENTED_INTRINSIC(X86, CRC32Update)
Evgeny Astigeevich15c5b972018-11-20 13:41:40 +00004792UNIMPLEMENTED_INTRINSIC(X86, CRC32UpdateBytes)
Evgeny Astigeevich776a7c22018-12-17 11:40:34 +00004793UNIMPLEMENTED_INTRINSIC(X86, CRC32UpdateByteBuffer)
xueliang.zhong9ce340f2019-01-22 17:46:09 +00004794UNIMPLEMENTED_INTRINSIC(X86, FP16ToFloat)
Vladimir Marko7f958e32019-10-24 09:03:58 +00004795UNIMPLEMENTED_INTRINSIC(X86, FP16ToHalf)
Usama Arifb9f02c22019-10-25 17:37:33 +01004796UNIMPLEMENTED_INTRINSIC(X86, FP16Floor)
Usama Arif665aac42019-10-29 11:13:18 +00004797UNIMPLEMENTED_INTRINSIC(X86, FP16Ceil)
Usama Arif681692b2019-10-30 16:23:26 +00004798UNIMPLEMENTED_INTRINSIC(X86, FP16Rint)
Usama Arif457e9fa2019-11-11 15:29:59 +00004799UNIMPLEMENTED_INTRINSIC(X86, FP16Greater)
4800UNIMPLEMENTED_INTRINSIC(X86, FP16GreaterEquals)
4801UNIMPLEMENTED_INTRINSIC(X86, FP16Less)
4802UNIMPLEMENTED_INTRINSIC(X86, FP16LessEquals)
Usama Arifecbdc072019-11-13 13:32:54 +00004803UNIMPLEMENTED_INTRINSIC(X86, FP16Compare)
Usama Arif39e29792019-11-15 10:53:29 +00004804UNIMPLEMENTED_INTRINSIC(X86, FP16Min)
4805UNIMPLEMENTED_INTRINSIC(X86, FP16Max)
Nikita Iashchenko745da802021-01-20 21:52:54 +00004806UNIMPLEMENTED_INTRINSIC(X86, MathMultiplyHigh)
Mark Mendell09ed1a32015-03-25 08:30:06 -04004807
Aart Bikff7d89c2016-11-07 08:49:28 -08004808UNIMPLEMENTED_INTRINSIC(X86, StringStringIndexOf);
4809UNIMPLEMENTED_INTRINSIC(X86, StringStringIndexOfAfter);
Aart Bik71bf7b42016-11-16 10:17:46 -08004810UNIMPLEMENTED_INTRINSIC(X86, StringBufferAppend);
4811UNIMPLEMENTED_INTRINSIC(X86, StringBufferLength);
4812UNIMPLEMENTED_INTRINSIC(X86, StringBufferToString);
Vladimir Markod4561172017-10-30 17:48:25 +00004813UNIMPLEMENTED_INTRINSIC(X86, StringBuilderAppendObject);
4814UNIMPLEMENTED_INTRINSIC(X86, StringBuilderAppendString);
4815UNIMPLEMENTED_INTRINSIC(X86, StringBuilderAppendCharSequence);
4816UNIMPLEMENTED_INTRINSIC(X86, StringBuilderAppendCharArray);
4817UNIMPLEMENTED_INTRINSIC(X86, StringBuilderAppendBoolean);
4818UNIMPLEMENTED_INTRINSIC(X86, StringBuilderAppendChar);
4819UNIMPLEMENTED_INTRINSIC(X86, StringBuilderAppendInt);
4820UNIMPLEMENTED_INTRINSIC(X86, StringBuilderAppendLong);
4821UNIMPLEMENTED_INTRINSIC(X86, StringBuilderAppendFloat);
4822UNIMPLEMENTED_INTRINSIC(X86, StringBuilderAppendDouble);
Aart Bik71bf7b42016-11-16 10:17:46 -08004823UNIMPLEMENTED_INTRINSIC(X86, StringBuilderLength);
4824UNIMPLEMENTED_INTRINSIC(X86, StringBuilderToString);
Aart Bikff7d89c2016-11-07 08:49:28 -08004825
Aart Bik0e54c012016-03-04 12:08:31 -08004826// 1.8.
Shalini Salomi Bodapati6545ee32021-11-02 20:01:06 +05304827
Aart Bik0e54c012016-03-04 12:08:31 -08004828UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndAddInt)
4829UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndAddLong)
4830UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndSetInt)
4831UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndSetLong)
4832UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndSetObject)
Aart Bik0e54c012016-03-04 12:08:31 -08004833
Andra Danciua0130e82020-07-23 12:34:56 +00004834UNIMPLEMENTED_INTRINSIC(X86, MethodHandleInvokeExact)
4835UNIMPLEMENTED_INTRINSIC(X86, MethodHandleInvoke)
Andra Danciua0130e82020-07-23 12:34:56 +00004836
Sorin Basca2f01e8e2021-06-18 06:44:07 +00004837// OpenJDK 11
4838UNIMPLEMENTED_INTRINSIC(X86, JdkUnsafeGetAndAddInt)
4839UNIMPLEMENTED_INTRINSIC(X86, JdkUnsafeGetAndAddLong)
4840UNIMPLEMENTED_INTRINSIC(X86, JdkUnsafeGetAndSetInt)
4841UNIMPLEMENTED_INTRINSIC(X86, JdkUnsafeGetAndSetLong)
4842UNIMPLEMENTED_INTRINSIC(X86, JdkUnsafeGetAndSetObject)
Sorin Basca507cf902021-10-06 12:04:56 +00004843UNIMPLEMENTED_INTRINSIC(X86, JdkUnsafeCompareAndSetObject)
Sorin Basca2f01e8e2021-06-18 06:44:07 +00004844
Aart Bik2f9fcc92016-03-01 15:16:54 -08004845UNREACHABLE_INTRINSICS(X86)
Roland Levillain4d027112015-07-01 15:41:14 +01004846
4847#undef __
4848
Mark Mendell09ed1a32015-03-25 08:30:06 -04004849} // namespace x86
4850} // namespace art