Skip to content

Commit d575ec5

Browse files
nick-armJornVernee
andcommitted
8275584: Incorrect stack spilling in CallArranger on MacOS/AArch64
Co-authored-by: Jorn Vernee <jvernee@openjdk.org> Reviewed-by: jvernee
1 parent 0714b72 commit d575ec5

File tree

5 files changed

+277
-57
lines changed

5 files changed

+277
-57
lines changed

src/hotspot/cpu/aarch64/foreignGlobals_aarch64.cpp

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -109,15 +109,16 @@ static void move_reg64(MacroAssembler* masm, int out_stk_bias,
109109
break;
110110
case StorageType::STACK:
111111
out_bias = out_stk_bias;
112-
case StorageType::FRAME_DATA:
112+
case StorageType::FRAME_DATA: {
113+
Address dest(sp, to_reg.offset() + out_bias);
113114
switch (to_reg.stack_size()) {
114-
// FIXME use correctly sized stores
115-
case 8: case 4: case 2: case 1:
116-
masm->str(from_reg, Address(sp, to_reg.offset() + out_stk_bias));
117-
break;
115+
case 8: masm->str (from_reg, dest); break;
116+
case 4: masm->strw(from_reg, dest); break;
117+
case 2: masm->strh(from_reg, dest); break;
118+
case 1: masm->strb(from_reg, dest); break;
118119
default: ShouldNotReachHere();
119120
}
120-
break;
121+
} break;
121122
default: ShouldNotReachHere();
122123
}
123124
}
@@ -130,10 +131,10 @@ static void move_stack(MacroAssembler* masm, Register tmp_reg, int in_stk_bias,
130131
case StorageType::INTEGER:
131132
assert(to_reg.segment_mask() == REG64_MASK, "only moves to 64-bit registers supported");
132133
switch (from_reg.stack_size()) {
133-
// FIXME use correctly sized loads
134-
case 8: case 4: case 2: case 1:
135-
masm->ldr(as_Register(to_reg), from_addr);
136-
break;
134+
case 8: masm->ldr (as_Register(to_reg), from_addr); break;
135+
case 4: masm->ldrw(as_Register(to_reg), from_addr); break;
136+
case 2: masm->ldrh(as_Register(to_reg), from_addr); break;
137+
case 1: masm->ldrb(as_Register(to_reg), from_addr); break;
137138
default: ShouldNotReachHere();
138139
}
139140
break;
@@ -151,18 +152,23 @@ static void move_stack(MacroAssembler* masm, Register tmp_reg, int in_stk_bias,
151152
break;
152153
case StorageType::STACK:
153154
out_bias = out_stk_bias;
154-
case StorageType::FRAME_DATA:
155-
// We assume 8 bytes stack size when converting from VMReg (Java CC)
156-
//assert(from_reg.stack_size() == to_reg.stack_size(), "must be same");
155+
case StorageType::FRAME_DATA: {
157156
switch (from_reg.stack_size()) {
158-
// FIXME use correctly sized loads & stores
159-
case 8: case 4: case 2: case 1:
160-
masm->ldr(tmp_reg, from_addr);
161-
masm->str(tmp_reg, Address(sp, to_reg.offset() + out_bias));
162-
break;
157+
case 8: masm->ldr (tmp_reg, from_addr); break;
158+
case 4: masm->ldrw(tmp_reg, from_addr); break;
159+
case 2: masm->ldrh(tmp_reg, from_addr); break;
160+
case 1: masm->ldrb(tmp_reg, from_addr); break;
163161
default: ShouldNotReachHere();
164162
}
165-
break;
163+
Address dest(sp, to_reg.offset() + out_bias);
164+
switch (to_reg.stack_size()) {
165+
case 8: masm->str (tmp_reg, dest); break;
166+
case 4: masm->strw(tmp_reg, dest); break;
167+
case 2: masm->strh(tmp_reg, dest); break;
168+
case 1: masm->strb(tmp_reg, dest); break;
169+
default: ShouldNotReachHere();
170+
}
171+
} break;
166172
default: ShouldNotReachHere();
167173
}
168174
}
@@ -174,17 +180,14 @@ static void move_v128(MacroAssembler* masm, int out_stk_bias,
174180
assert(to_reg.segment_mask() == V128_MASK, "only moves to v128 registers supported");
175181
masm->fmovd(as_FloatRegister(to_reg), from_reg);
176182
break;
177-
case StorageType::STACK:
178-
switch(to_reg.stack_size()) {
179-
case 8:
180-
masm->strd(from_reg, Address(sp, to_reg.offset() + out_stk_bias));
181-
break;
182-
case 4:
183-
masm->strs(from_reg, Address(sp, to_reg.offset() + out_stk_bias));
184-
break;
183+
case StorageType::STACK: {
184+
Address dest(sp, to_reg.offset() + out_stk_bias);
185+
switch (to_reg.stack_size()) {
186+
case 8: masm->strd(from_reg, dest); break;
187+
case 4: masm->strs(from_reg, dest); break;
185188
default: ShouldNotReachHere();
186189
}
187-
break;
190+
} break;
188191
default: ShouldNotReachHere();
189192
}
190193
}

src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/CallArranger.java

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
3-
* Copyright (c) 2019, 2021, Arm Limited. All rights reserved.
3+
* Copyright (c) 2019, 2022, Arm Limited. All rights reserved.
44
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
55
*
66
* This code is free software; you can redistribute it and/or modify it
@@ -186,21 +186,13 @@ public StorageCalculator(boolean forArguments) {
186186
this.forArguments = forArguments;
187187
}
188188

189+
void alignStack(long alignment) {
190+
stackOffset = Utils.alignUp(stackOffset, alignment);
191+
}
192+
189193
VMStorage stackAlloc(long size, long alignment) {
190194
assert forArguments : "no stack returns";
191-
// Implementation limit: each arg must take up at least an 8 byte stack slot (on the Java side)
192-
// There is currently no way to address stack offsets that are not multiples of 8 bytes
193-
// The VM can only address multiple-of-4-bytes offsets, which is also not good enough for some ABIs
194-
// see JDK-8283462 and related issues
195-
long stackSlotAlignment = Math.max(alignment, STACK_SLOT_SIZE);
196-
long alignedStackOffset = Utils.alignUp(stackOffset, stackSlotAlignment);
197-
// macos-aarch64 ABI potentially requires addressing stack offsets that are not multiples of 8 bytes
198-
// Reject such call types here, to prevent undefined behavior down the line
199-
// Reject if the above stack-slot-aligned offset does not match the offset the ABI really wants
200-
// Except for variadic arguments, which _are_ passed at 8-byte-aligned offsets
201-
if (requiresSubSlotStackPacking() && alignedStackOffset != Utils.alignUp(stackOffset, alignment)
202-
&& !forVarArgs) // varargs are given a pass on all aarch64 ABIs
203-
throw new UnsupportedOperationException("Call type not supported on this platform");
195+
long alignedStackOffset = Utils.alignUp(stackOffset, alignment);
204196

205197
short encodedSize = (short) size;
206198
assert (encodedSize & 0xFFFF) == size;
@@ -212,7 +204,10 @@ VMStorage stackAlloc(long size, long alignment) {
212204
}
213205

214206
VMStorage stackAlloc(MemoryLayout layout) {
215-
return stackAlloc(layout.byteSize(), layout.byteAlignment());
207+
long stackSlotAlignment = requiresSubSlotStackPacking() && !forVarArgs
208+
? layout.byteAlignment()
209+
: Math.max(layout.byteAlignment(), STACK_SLOT_SIZE);
210+
return stackAlloc(layout.byteSize(), stackSlotAlignment);
216211
}
217212

218213
VMStorage[] regAlloc(int type, int count) {
@@ -245,6 +240,26 @@ VMStorage nextStorage(int type, MemoryLayout layout) {
245240
return storage[0];
246241
}
247242

243+
VMStorage[] nextStorageForHFA(GroupLayout group) {
244+
final int nFields = group.memberLayouts().size();
245+
VMStorage[] regs = regAlloc(StorageType.VECTOR, nFields);
246+
if (regs == null && requiresSubSlotStackPacking() && !forVarArgs) {
247+
// For the ABI variants that pack arguments spilled to the
248+
// stack, HFA arguments are spilled as if their individual
249+
// fields had been allocated separately rather than as if the
250+
// struct had been spilled as a whole.
251+
252+
VMStorage[] slots = new VMStorage[nFields];
253+
for (int i = 0; i < nFields; i++) {
254+
slots[i] = stackAlloc(group.memberLayouts().get(i));
255+
}
256+
257+
return slots;
258+
} else {
259+
return regs;
260+
}
261+
}
262+
248263
void adjustForVarArgs() {
249264
// This system passes all variadic parameters on the stack. Ensure
250265
// no further arguments are allocated to registers.
@@ -280,6 +295,12 @@ protected void spillStructUnbox(Binding.Builder bindings, MemoryLayout layout) {
280295
.vmStore(storage, type);
281296
offset += STACK_SLOT_SIZE;
282297
}
298+
299+
if (requiresSubSlotStackPacking()) {
300+
// Pad to the next stack slot boundary instead of packing
301+
// additional arguments into the unused space.
302+
storageCalculator.alignStack(STACK_SLOT_SIZE);
303+
}
283304
}
284305

285306
protected void spillStructBox(Binding.Builder bindings, MemoryLayout layout) {
@@ -299,6 +320,12 @@ protected void spillStructBox(Binding.Builder bindings, MemoryLayout layout) {
299320
.bufferStore(offset, type);
300321
offset += STACK_SLOT_SIZE;
301322
}
323+
324+
if (requiresSubSlotStackPacking()) {
325+
// Pad to the next stack slot boundary instead of packing
326+
// additional arguments into the unused space.
327+
storageCalculator.alignStack(STACK_SLOT_SIZE);
328+
}
302329
}
303330

304331
abstract List<Binding> getBindings(Class<?> carrier, MemoryLayout layout);
@@ -360,8 +387,7 @@ List<Binding> getBindings(Class<?> carrier, MemoryLayout layout) {
360387
case STRUCT_HFA: {
361388
assert carrier == MemorySegment.class;
362389
GroupLayout group = (GroupLayout)layout;
363-
VMStorage[] regs = storageCalculator.regAlloc(
364-
StorageType.VECTOR, group.memberLayouts().size());
390+
VMStorage[] regs = storageCalculator.nextStorageForHFA(group);
365391
if (regs != null) {
366392
long offset = 0;
367393
for (int i = 0; i < group.memberLayouts().size(); i++) {
@@ -458,8 +484,7 @@ List<Binding> getBindings(Class<?> carrier, MemoryLayout layout) {
458484
assert carrier == MemorySegment.class;
459485
bindings.allocate(layout);
460486
GroupLayout group = (GroupLayout) layout;
461-
VMStorage[] regs = storageCalculator.regAlloc(
462-
StorageType.VECTOR, group.memberLayouts().size());
487+
VMStorage[] regs = storageCalculator.nextStorageForHFA(group);
463488
if (regs != null) {
464489
long offset = 0;
465490
for (int i = 0; i < group.memberLayouts().size(); i++) {

test/jdk/ProblemList.txt

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -479,13 +479,6 @@ java/beans/XMLEncoder/Test6570354.java 8015593 macosx-all
479479

480480
############################################################################
481481

482-
# jdk_foreign
483-
484-
java/foreign/TestUpcallStack.java 8275584 macosx-aarch64
485-
java/foreign/TestDowncallStack.java 8275584 macosx-aarch64
486-
487-
############################################################################
488-
489482
# jdk_lang
490483

491484
java/lang/ProcessHandle/InfoTest.java 8211847 aix-ppc64

test/jdk/java/foreign/callarranger/CallArrangerTestBase.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,17 @@
3232
public class CallArrangerTestBase {
3333

3434
public static void checkArgumentBindings(CallingSequence callingSequence, Binding[][] argumentBindings) {
35-
assertEquals(callingSequence.argumentBindingsCount(), argumentBindings.length);
35+
assertEquals(callingSequence.argumentBindingsCount(), argumentBindings.length,
36+
callingSequence.asString() + " != " + Arrays.deepToString(argumentBindings));
3637

3738
for (int i = 0; i < callingSequence.argumentBindingsCount(); i++) {
3839
List<Binding> actual = callingSequence.argumentBindings(i);
3940
Binding[] expected = argumentBindings[i];
40-
assertEquals(actual, Arrays.asList(expected));
41+
assertEquals(actual, Arrays.asList(expected), "bindings at: " + i + ": " + actual + " != " + Arrays.toString(expected));
4142
}
4243
}
4344

4445
public static void checkReturnBindings(CallingSequence callingSequence, Binding[] returnBindings) {
45-
assertEquals(callingSequence.returnBindings(), Arrays.asList(returnBindings));
46+
assertEquals(callingSequence.returnBindings(), Arrays.asList(returnBindings), callingSequence.returnBindings() + " != " + Arrays.toString(returnBindings));
4647
}
4748
}

0 commit comments

Comments
 (0)