Skip to content

Commit

Permalink
Merge pull request #6761 from knn-k/aarch64linkage5
Browse files Browse the repository at this point in the history
AArch64: Implement ARM64PrivateLinkage::buildIndirectDispatch()
  • Loading branch information
0xdaryl committed Sep 25, 2019
2 parents 88b76cc + f07b9ea commit 54b1c4f
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 10 deletions.
162 changes: 159 additions & 3 deletions runtime/compiler/aarch64/codegen/ARM64PrivateLinkage.cpp
Expand Up @@ -646,17 +646,173 @@ TR::Register *TR::ARM64PrivateLinkage::buildDirectDispatch(TR::Node *callNode)
break;
default:
retReg = NULL;
TR_ASSERT(false, "Unsupported direct call Opcode.");
TR_ASSERT_FATAL(false, "Unsupported direct call Opcode.");
}

callNode->setRegister(retReg);
return retReg;
}

static TR::Register *evaluateUpToVftChild(TR::Node *callNode, TR::CodeGenerator *cg)
{
TR::Register *vftReg = NULL;
if (callNode->getFirstArgumentIndex() == 1)
{
TR::Node *child = callNode->getFirstChild();
vftReg = cg->evaluate(child);
cg->decReferenceCount(child);
}
TR_ASSERT_FATAL(vftReg != NULL, "Failed to find vft child.");
return vftReg;
}

void TR::ARM64PrivateLinkage::buildVirtualDispatch(TR::Node *callNode,
TR::RegisterDependencyConditions *dependencies,
uint32_t argSize)
{
TR::Register *x0 = dependencies->searchPreConditionRegister(TR::RealRegister::x0);
TR::Register *x9 = dependencies->searchPreConditionRegister(TR::RealRegister::x9);

TR::SymbolReference *methodSymRef = callNode->getSymbolReference();
TR::MethodSymbol *methodSymbol = methodSymRef->getSymbol()->castToMethodSymbol();
TR::LabelSymbol *doneLabel = NULL;

TR::Instruction *gcPoint;

TR_J9VMBase *fej9 = (TR_J9VMBase *)(comp()->fe());

// Computed calls
//
if (methodSymbol->isComputed())
{
// ToDo: Implement this path (Eclipse OpenJ9 Issue #7023)
comp()->failCompilation<TR::AssertionFailure>("ComputedCall");
}

// Virtual and interface calls
//
TR_ASSERT_FATAL(methodSymbol->isVirtual() || methodSymbol->isInterface(), "Unexpected method type");

void *thunk = fej9->getJ2IThunk(methodSymbol->getMethod(), comp());
if (!thunk)
thunk = fej9->setJ2IThunk(methodSymbol->getMethod(), TR::ARM64CallSnippet::generateVIThunk(callNode, argSize, cg()), comp());

if (methodSymbol->isVirtual())
{
TR::MemoryReference *tempMR;
TR::Register *vftReg = evaluateUpToVftChild(callNode, cg());
TR::addDependency(dependencies, vftReg, TR::RealRegister::NoReg, TR_GPR, cg());

if (methodSymRef->isUnresolved() || comp()->compileRelocatableCode())
{
doneLabel = generateLabelSymbol(cg());

TR::LabelSymbol *vcSnippetLabel = generateLabelSymbol(cg());
TR::ARM64VirtualUnresolvedSnippet *vcSnippet =
new (trHeapMemory())
TR::ARM64VirtualUnresolvedSnippet(cg(), callNode, vcSnippetLabel, argSize, doneLabel, (uint8_t *)thunk);
cg()->addSnippet(vcSnippet);

TR::Register *dstReg = cg()->allocateRegister();

// The following instructions are modified by _virtualUnresolvedHelper
// in aarch64/runtime/PicBuilder.spp to load the vTable index in x9
generateTrg1ImmInstruction(cg(), TR::InstOpCode::movzx, callNode, x9, 0);
generateTrg1ImmInstruction(cg(), TR::InstOpCode::movkx, callNode, x9, TR::MOV_LSL16);
generateTrg1Src1ImmInstruction(cg(), TR::InstOpCode::sbfmx, callNode, x9, x9, 0x1F); // sxtw x9, w9
tempMR = new (trHeapMemory()) TR::MemoryReference(vftReg, x9, cg());
generateTrg1MemInstruction(cg(), TR::InstOpCode::ldroffx, callNode, dstReg, tempMR);
gcPoint = generateLabelInstruction(cg(), TR::InstOpCode::b, callNode, vcSnippetLabel, dependencies);

cg()->stopUsingRegister(dstReg);
}
else
{
int32_t offset = methodSymRef->getOffset();
TR_ASSERT(offset < 0, "Unexpected positive offset for virtual call");

// jitVTableIndex() in oti/JITInterface.hpp assumes the instruction sequence below
if (offset >= -65536)
{
generateTrg1ImmInstruction(cg(), TR::InstOpCode::movnx, callNode, x9, ~offset & 0xFFFF);
}
else
{
generateTrg1ImmInstruction(cg(), TR::InstOpCode::movzx, callNode, x9, offset & 0xFFFF);
generateTrg1ImmInstruction(cg(), TR::InstOpCode::movkx, callNode, x9,
(((offset >> 16) & 0xFFFF) | TR::MOV_LSL16));
generateTrg1Src1ImmInstruction(cg(), TR::InstOpCode::sbfmx, callNode, x9, x9, 0x1F); // sxtw x9, w9
}
tempMR = new (trHeapMemory()) TR::MemoryReference(vftReg, x9, cg());
generateTrg1MemInstruction(cg(), TR::InstOpCode::ldroffx, callNode, x9, tempMR);
gcPoint = generateRegBranchInstruction(cg(), TR::InstOpCode::blr, callNode, x9, dependencies);
}
}
else
{
// interface calls
// ToDo: Inline interface dispatch
doneLabel = generateLabelSymbol(cg());

TR::LabelSymbol *ifcSnippetLabel = generateLabelSymbol(cg());
TR::ARM64InterfaceCallSnippet *ifcSnippet =
new (trHeapMemory())
TR::ARM64InterfaceCallSnippet(cg(), callNode, ifcSnippetLabel, argSize, doneLabel, (uint8_t *)thunk);
cg()->addSnippet(ifcSnippet);

gcPoint = generateLabelInstruction(cg(), TR::InstOpCode::b, callNode, ifcSnippetLabel, dependencies);
}

gcPoint->ARM64NeedsGCMap(cg(), getProperties().getPreservedRegisterMapForGC());

if (doneLabel)
generateLabelInstruction(cg(), TR::InstOpCode::label, callNode, doneLabel, dependencies);

return;
}

TR::Register *TR::ARM64PrivateLinkage::buildIndirectDispatch(TR::Node *callNode)
{
TR_UNIMPLEMENTED();
return NULL;
const TR::ARM64LinkageProperties &pp = getProperties();
TR::RealRegister *sp = cg()->machine()->getRealRegister(pp.getStackPointerRegister());

TR::RegisterDependencyConditions *dependencies =
new (trHeapMemory()) TR::RegisterDependencyConditions(
pp.getNumberOfDependencyGPRegisters(),
pp.getNumberOfDependencyGPRegisters(), trMemory());

int32_t argSize = buildArgs(callNode, dependencies);

buildVirtualDispatch(callNode, dependencies, argSize);
cg()->machine()->setLinkRegisterKilled(true);

TR::Register *retReg;
switch(callNode->getOpCodeValue())
{
case TR::icalli:
retReg = dependencies->searchPostConditionRegister(
pp.getIntegerReturnRegister());
break;
case TR::lcalli:
case TR::acalli:
retReg = dependencies->searchPostConditionRegister(
pp.getLongReturnRegister());
break;
case TR::fcalli:
case TR::dcalli:
retReg = dependencies->searchPostConditionRegister(
pp.getFloatReturnRegister());
break;
case TR::calli:
retReg = NULL;
break;
default:
retReg = NULL;
TR_ASSERT_FATAL(false, "Unsupported indirect call Opcode.");
}

callNode->setRegister(retReg);
return retReg;
}

int32_t TR::ARM64HelperLinkage::buildArgs(TR::Node *callNode,
Expand Down
11 changes: 11 additions & 0 deletions runtime/compiler/aarch64/codegen/ARM64PrivateLinkage.hpp
Expand Up @@ -162,6 +162,17 @@ class ARM64PrivateLinkage : public TR::Linkage
* @param[in] node : caller node
*/
virtual TR::Register *buildIndirectDispatch(TR::Node *callNode);

/**
* @brief Builds virtual dispatch to method
* @param[in] node : caller node
* @param[in] dependencies : register dependency conditions
* @param[in] argSize : size of arguments
*/
virtual void buildVirtualDispatch(
TR::Node *callNode,
TR::RegisterDependencyConditions *dependencies,
uint32_t argSize);
};

class ARM64HelperLinkage : public TR::ARM64PrivateLinkage
Expand Down
34 changes: 27 additions & 7 deletions runtime/oti/JITInterface.hpp
Expand Up @@ -25,11 +25,6 @@

#include "j9port.h"

/* TODO: Implement jitVTableIndex() to remove this #include */
#if defined(J9VM_ARCH_AARCH64)
#include "assert.h"
#endif

extern "C" {

#if defined(J9VM_ARCH_X86)
Expand Down Expand Up @@ -192,8 +187,33 @@ class VM_JITInterface
jitVTableIndex = 0 - (instruction & 0x00000FFF);
}
#elif defined(J9VM_ARCH_AARCH64)
/* TODO: Implement this */
assert(0);
/* Virtual call instructions
* ...
* movn x9, vTableOffset (if within 16 bit)
* ldr x9, [vftReg, x9]
* blr x9
* <- return address points here
* See TR::ARM64PrivateLinkage::buildVirtualDispatch()
*/
U_32 movInstr1 = ((U_32*)jitReturnAddress)[-3];
U_32 ldrInstr = ((U_32*)jitReturnAddress)[-2];
if ((ldrInstr & 0xFFFFF81F) == 0xF8696809) { // ldr x9, [vftReg, x9]
if ((movInstr1 & 0xFFE0001F) == 0x92800009) {
// movn x9, vTableOffset (negated)
jitVTableIndex = (UDATA)(-(I_64)((movInstr1 >> 5) & 0xFFFF)-1);
} else {
// movz x9, vTableOffset (lower 16 bits)
// movk x9, vTableOffset, LSL #16 (upper 16 bits)
// sxtw x9, w9
U_32 movInstr2 = ((U_32*)jitReturnAddress)[-4];
movInstr1 = ((U_32*)jitReturnAddress)[-5];
if ((movInstr1 & 0xFFE0001F) == 0xD2800009 &&
(movInstr2 & 0xFFE0001F) == 0xF2A00009) {
I_32 idx = ((movInstr2 << 11) & 0xFFFF0000) | ((movInstr1 >> 5) & 0xFFFF);
jitVTableIndex = (UDATA)((I_64)idx);
}
}
}
#elif defined(J9VM_ARCH_S390)
/* The vtable index is always in the register */
#else
Expand Down

0 comments on commit 54b1c4f

Please sign in to comment.