Skip to content

Commit 425601f

Browse files
committed
Z: Move off-heap zero length allocation OOL
Refactored off-heap 2D array allocation logic to improve clarity and maintainability on IBM Z platform. Previously, STOCG was used to store the data address only when the length was non-zero, relying on condition codes to control flow. This approach introduced hidden dependencies on allocator condition codes, making the code harder to read and prone to future bugs. Updated the implementation to allocate non-zero length off-heap arrays inline, while handling zero-length arrays out of line. This removes reliance on condition codes for allocation decisions, simplifies control flow, and ensures consistent behavior across allocation paths. signed-off-by: Ehsan Kiani Far <ehsan.kianifar@gmail.com>
1 parent 03e3d37 commit 425601f

File tree

1 file changed

+54
-32
lines changed

1 file changed

+54
-32
lines changed

runtime/compiler/z/codegen/J9TreeEvaluator.cpp

Lines changed: 54 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4905,6 +4905,7 @@ static TR::Register * generateMultianewArrayWithInlineAllocators(TR::Node *node,
49054905
}
49064906

49074907
TR::LabelSymbol *zeroSecondDimLabel = generateLabelSymbol(cg);
4908+
49084909
// Bypass dim2 size calculation if the size is zero.
49094910
cursor = generateS390BranchInstruction(cg, TR::InstOpCode::BRC, TR::InstOpCode::COND_BZ, node, zeroSecondDimLabel, cursor);
49104911
// Call helper if the length is negative or length * componentSize is larger that INT32_MAX (overflow).
@@ -5017,16 +5018,19 @@ static TR::Register * generateMultianewArrayWithInlineAllocators(TR::Node *node,
50175018

50185019
#if defined(J9VM_GC_SPARSE_HEAP_ALLOCATION)
50195020
bool isOffHeapAllocationEnabled = TR::Compiler->om.isOffHeapAllocationEnabled();
5021+
TR::LabelSymbol *zeroLengthSecondDimLabel = NULL;
50205022
if (isOffHeapAllocationEnabled)
50215023
{
50225024
// Store first element's address in data address field.
50235025
cursor = generateRXInstruction(cg, TR::InstOpCode::STG, node, sizeReg, generateS390MemoryReference(resultReg, fej9->getOffsetOfContiguousDataAddrField(), cg), cursor);
5024-
// Subtract the header size from the dim2 size, so we can move to the next leaf by adding this value to the address of the leaf's first element.
5025-
cursor = generateRILInstruction(cg, TR::InstOpCode::SLFI, node, dim2SizeReg, (int32_t)TR::Compiler->om.contiguousArrayHeaderSizeInBytes(), cursor);
5026+
// If the second dimension length is zero, allocate the leaf array OOL.
5027+
zeroLengthSecondDimLabel = generateLabelSymbol(cg);
50265028
// The length of the second dimension is stored in the upper 32 bits of the scratch register.
5027-
// Comparing the full 64-bit scratch register with its lower 32 bits sets the condition code to COND_BNE if the second dimension is non-zero.
5029+
// Comparing the full 64-bit scratch register with its lower 32 bits sets the condition code to COND_BE if the second dimension is zero.
50285030
cursor = generateRREInstruction(cg, TR::InstOpCode::CGFR, node, scratchReg, scratchReg, cursor);
5029-
// The condition code has already been set and must remain unchanged during the second dimension allocation loop in this code path.
5031+
cursor = generateS390BranchInstruction(cg, TR::InstOpCode::BRC, TR::InstOpCode::COND_BE, node, zeroLengthSecondDimLabel, cursor);
5032+
// Subtract the header size from the dim2 size, so we can move to the next leaf by adding this value to the address of the leaf's first element.
5033+
cursor = generateRILInstruction(cg, TR::InstOpCode::SLFI, node, dim2SizeReg, (int32_t)TR::Compiler->om.contiguousArrayHeaderSizeInBytes(), cursor);
50305034
}
50315035
#endif /* J9VM_GC_SPARSE_HEAP_ALLOCATION */
50325036

@@ -5040,58 +5044,76 @@ static TR::Register * generateMultianewArrayWithInlineAllocators(TR::Node *node,
50405044
cursor = generateRXInstruction(cg, TR::InstOpCode::STFH, node, scratchReg, generateS390MemoryReference(dim1SizeReg,
50415045
static_cast<int32_t>(TR::Compiler->om.offsetOfContiguousArraySizeField()), cg), cursor);
50425046
int32_t shiftAmount = TR::Compiler->om.compressedReferenceShift();
5043-
5044-
if (shiftAmount == 0)
5047+
if (shiftAmount > 0)
50455048
{
5046-
// Store the leaf address in the relevant element of the first dim array.
5049+
// Calculate the compressed reference of leaf array in the higher 32bits of dim2SizeReg.
5050+
cursor = generateRIEInstruction(cg, TR::InstOpCode::RISBG, node, dim2SizeReg, dim1SizeReg, 0, 31, 32-shiftAmount, cursor);
5051+
// Store the compressed 32 bit leaf address in the relevant element of the first dim array.
5052+
cursor = generateRXInstruction(cg, TR::InstOpCode::STFH, node, dim2SizeReg, generateS390MemoryReference(sizeReg, 0, cg), cursor);
5053+
}
5054+
else
5055+
{
5056+
// Store the 32 bit leaf address in the relevant element of the first dim array.
50475057
cursor = generateRXInstruction(cg, (TR::Compiler->om.compressObjectReferences() ? TR::InstOpCode::ST : TR::InstOpCode::STG), node, dim1SizeReg,
50485058
generateS390MemoryReference(sizeReg, 0, cg), cursor);
50495059
}
50505060

50515061
#if defined(J9VM_GC_SPARSE_HEAP_ALLOCATION)
50525062
if (isOffHeapAllocationEnabled)
50535063
{
5054-
if (shiftAmount > 0)
5055-
{
5056-
// Compress the leaf array reference and store it in the first element of the dimension array.
5057-
// Avoid using any instructions that could affect the branch condition in this code path.
5058-
cursor = generateRSInstruction(cg, TR::InstOpCode::SRLG, node, dim1SizeReg, dim1SizeReg, shiftAmount, cursor);
5059-
cursor = generateRXInstruction(cg, TR::InstOpCode::ST, node, dim1SizeReg, generateS390MemoryReference(sizeReg, 0, cg), cursor);
5060-
// Decompress the leaf array reference.
5061-
cursor = generateRSInstruction(cg, TR::InstOpCode::SLLG, node, dim1SizeReg, dim1SizeReg, shiftAmount, cursor);
5062-
}
50635064
// Load the first element address.
50645065
cursor = generateRXInstruction(cg, TR::InstOpCode::LA, node, dim1SizeReg,
50655066
generateS390MemoryReference(dim1SizeReg, TR::Compiler->om.contiguousArrayHeaderSizeInBytes(), cg), cursor);
5066-
// Store the address of the first element in the data address field only if the second dimension length is non-zero.
5067-
cursor = generateRSInstruction(cg, TR::InstOpCode::STOCG, node, dim1SizeReg, getMaskForBranchCondition(TR::InstOpCode::COND_BNE),
5068-
generateS390MemoryReference(dim1SizeReg, (fej9->getOffsetOfContiguousDataAddrField() - TR::Compiler->om.contiguousArrayHeaderSizeInBytes()), cg), cursor);
5069-
// Load the next leaf address in dim1SizeReg.
5070-
cursor = generateRXInstruction(cg, TR::InstOpCode::LA, node, dim1SizeReg, generateS390MemoryReference(dim1SizeReg, dim2SizeReg, 0, cg), cursor);
5067+
// Store the first element address in data address field.
5068+
cursor = generateRXInstruction(cg, TR::InstOpCode::STG, node, dim1SizeReg, generateS390MemoryReference(dim1SizeReg,
5069+
(fej9->getOffsetOfContiguousDataAddrField() - TR::Compiler->om.contiguousArrayHeaderSizeInBytes()), cg), cursor);
50715070
}
5072-
else
50735071
#endif /* J9VM_GC_SPARSE_HEAP_ALLOCATION */
5072+
5073+
// Load the next leaf address in dim1SizeReg.
5074+
cursor = generateRREInstruction(cg, TR::InstOpCode::ALGFR, node, dim1SizeReg, dim2SizeReg, cursor);
5075+
// Load the next element address of the first dim array in sizeReg.
5076+
cursor = generateRXInstruction(cg, TR::InstOpCode::LA, node, sizeReg, generateS390MemoryReference(sizeReg, elementSize, cg), cursor);
5077+
cursor = generateS390BranchInstruction(cg, TR::InstOpCode::BRCT, node, scratchReg, secondDimLabel, cursor);
5078+
cursor = generateS390BranchInstruction(cg, TR::InstOpCode::BRC, TR::InstOpCode::COND_B, node, controlFlowEndLabel, cursor);
5079+
5080+
/********************************************* OOL zero length offheap leaf allocator *********************************************/
5081+
#if defined(J9VM_GC_SPARSE_HEAP_ALLOCATION)
5082+
if (isOffHeapAllocationEnabled)
50745083
{
5084+
TR_S390OutOfLineCodeSection *zeroLengthSecondDimAlloc = new (cg->trHeapMemory()) TR_S390OutOfLineCodeSection(zeroLengthSecondDimLabel, controlFlowEndLabel, cg);
5085+
cg->getS390OutOfLineCodeSectionList().push_front(zeroLengthSecondDimAlloc);
5086+
zeroLengthSecondDimAlloc->swapInstructionListsWithCompilation();
5087+
generateS390LabelInstruction(cg, TR::InstOpCode::label, node, zeroLengthSecondDimLabel);
5088+
5089+
// Store the class field.
5090+
generateRXInstruction(cg, (compressedObjectHeaders ? TR::InstOpCode::ST : TR::InstOpCode::STG), node, classReg,
5091+
generateS390MemoryReference(dim1SizeReg, static_cast<int32_t>(TR::Compiler->om.offsetOfObjectVftField()), cg));
5092+
// Store the array length.
50755093
if (shiftAmount > 0)
50765094
{
50775095
// Calculate the compressed reference of leaf array in the higher 32bits of dim2SizeReg.
5078-
cursor = generateRIEInstruction(cg, TR::InstOpCode::RISBG, node, dim2SizeReg, dim1SizeReg, 0, 31, 32-shiftAmount, cursor);
5096+
generateRIEInstruction(cg, TR::InstOpCode::RISBG, node, dim2SizeReg, dim1SizeReg, 0, 31, 32-shiftAmount);
50795097
// Store the compressed 32 bit leaf address in the relevant element of the first dim array.
5080-
cursor = generateRXInstruction(cg, TR::InstOpCode::STFH, node, dim2SizeReg, generateS390MemoryReference(sizeReg, 0, cg), cursor);
5081-
// Load the next leaf address in dim1SizeReg.
5082-
cursor = generateRREInstruction(cg, TR::InstOpCode::ALGFR, node, dim1SizeReg, dim2SizeReg, cursor);
5098+
generateRXInstruction(cg, TR::InstOpCode::STFH, node, dim2SizeReg, generateS390MemoryReference(sizeReg, 0, cg));
50835099
}
50845100
else
50855101
{
5086-
// Load the next leaf address in dim1SizeReg.
5087-
cursor = generateRXInstruction(cg, TR::InstOpCode::LA, node, dim1SizeReg, generateS390MemoryReference(dim1SizeReg, dim2SizeReg, 0, cg), cursor);
5102+
// Store the 32 bit leaf address in the relevant element of the first dim array.
5103+
generateRXInstruction(cg, (TR::Compiler->om.compressObjectReferences() ? TR::InstOpCode::ST : TR::InstOpCode::STG), node, dim1SizeReg,
5104+
generateS390MemoryReference(sizeReg, 0, cg));
50885105
}
5106+
// Load the next leaf address in dim1SizeReg.
5107+
generateRREInstruction(cg, TR::InstOpCode::ALGFR, node, dim1SizeReg, dim2SizeReg);
5108+
// Load the next element address of the first dim array in sizeReg.
5109+
generateRXInstruction(cg, TR::InstOpCode::LA, node, sizeReg, generateS390MemoryReference(sizeReg, elementSize, cg));
5110+
generateS390BranchInstruction(cg, TR::InstOpCode::BRCT, node, scratchReg, zeroLengthSecondDimLabel);
5111+
5112+
generateS390BranchInstruction(cg, TR::InstOpCode::BRC, TR::InstOpCode::COND_BRC, node, controlFlowEndLabel);
5113+
zeroLengthSecondDimAlloc->swapInstructionListsWithCompilation();
50895114
}
5115+
#endif /* J9VM_GC_SPARSE_HEAP_ALLOCATION */
50905116

5091-
// Load the next element address of the first dim array in sizeReg.
5092-
cursor = generateRXInstruction(cg, TR::InstOpCode::LA, node, sizeReg, generateS390MemoryReference(sizeReg, elementSize, cg), cursor);
5093-
cursor = generateS390BranchInstruction(cg, TR::InstOpCode::BRCT, node, scratchReg, secondDimLabel, cursor);
5094-
cursor = generateS390BranchInstruction(cg, TR::InstOpCode::BRC, TR::InstOpCode::COND_B, node, controlFlowEndLabel, cursor);
50955117
iComment("Allocation done!");
50965118

50975119
TR::LabelSymbol *slowPathLabel = generateLabelSymbol(cg);

0 commit comments

Comments
 (0)