Skip to content

Commit

Permalink
[VM] Add PcRelativeCall / PcRelativeJump patterns & assembler support
Browse files Browse the repository at this point in the history
Issue #33274

Change-Id: I0297c4bb502a1af28e3cf16646eade49f4cd0676
Reviewed-on: https://dart-review.googlesource.com/c/81827
Commit-Queue: Martin Kustermann <[email protected]>
Reviewed-by: Vyacheslav Egorov <[email protected]>
  • Loading branch information
mkustermann authored and [email protected] committed Nov 1, 2018
1 parent 2939660 commit 9b9a035
Show file tree
Hide file tree
Showing 19 changed files with 297 additions and 26 deletions.
5 changes: 5 additions & 0 deletions runtime/vm/compiler/assembler/assembler_arm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3359,6 +3359,11 @@ void Assembler::TryAllocateArray(intptr_t cid,
}
}

void Assembler::GenerateUnRelocatedPcRelativeCall() {
// Emit "blr <offset>".
EmitType5(AL, 0x686868, /*link=*/true);
}

void Assembler::Stop(const char* message) {
if (FLAG_print_stop_message) {
PushList((1 << R0) | (1 << IP) | (1 << LR)); // Preserve R0, IP, LR.
Expand Down
13 changes: 13 additions & 0 deletions runtime/vm/compiler/assembler/assembler_arm.h
Original file line number Diff line number Diff line change
Expand Up @@ -1097,6 +1097,19 @@ class Assembler : public AssemblerBase {
Register temp1,
Register temp2);

// This emits an PC-relative call of the form "blr <offset>". The offset
// is not yet known and needs therefore relocation to the right place before
// the code can be used.
//
// The neccessary information for the "linker" (i.e. the relocation
// information) is stored in [RawCode::static_calls_target_table_]: an entry
// of the form
//
// (Code::kPcRelativeCall & pc_offset, <target-code>, <target-function>)
//
// will be used during relocation to fix the offset.
void GenerateUnRelocatedPcRelativeCall();

// Emit data (e.g encoded instruction or immediate) in instruction stream.
void Emit(int32_t value);

Expand Down
5 changes: 5 additions & 0 deletions runtime/vm/compiler/assembler/assembler_arm64.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1499,6 +1499,11 @@ void Assembler::TryAllocateArray(intptr_t cid,
}
}

void Assembler::GenerateUnRelocatedPcRelativeCall() {
// Emit "bl <offset>".
EmitUnconditionalBranchOp(BL, 0x686868);
}

Address Assembler::ElementAddressForIntIndex(bool is_external,
intptr_t cid,
intptr_t index_scale,
Expand Down
35 changes: 24 additions & 11 deletions runtime/vm/compiler/assembler/assembler_arm64.h
Original file line number Diff line number Diff line change
Expand Up @@ -1561,6 +1561,19 @@ class Assembler : public AssemblerBase {
Register temp1,
Register temp2);

// This emits an PC-relative call of the form "bl <offset>". The offset
// is not yet known and needs therefore relocation to the right place before
// the code can be used.
//
// The neccessary information for the "linker" (i.e. the relocation
// information) is stored in [RawCode::static_calls_target_table_]: an entry
// of the form
//
// (Code::kPcRelativeCall & pc_offset, <target-code>, <target-function>)
//
// will be used during relocation to fix the offset.
void GenerateUnRelocatedPcRelativeCall();

Address ElementAddressForIntIndex(bool is_external,
intptr_t cid,
intptr_t index_scale,
Expand Down Expand Up @@ -1592,6 +1605,17 @@ class Assembler : public AssemblerBase {
Register tmp,
OperandSize sz);

static int32_t EncodeImm26BranchOffset(int64_t imm, int32_t instr) {
const int32_t imm32 = static_cast<int32_t>(imm);
const int32_t off = (((imm32 >> 2) << kImm26Shift) & kImm26Mask);
return (instr & ~kImm26Mask) | off;
}

static int64_t DecodeImm26BranchOffset(int32_t instr) {
const int32_t off = (((instr & kImm26Mask) >> kImm26Shift) << 6) >> 4;
return static_cast<int64_t>(off);
}

private:
bool use_far_branches_;

Expand Down Expand Up @@ -1796,17 +1820,6 @@ class Assembler : public AssemblerBase {
return (instr & ~B24) | (cond == EQ ? B24 : 0); // tbz : tbnz
}

int32_t EncodeImm26BranchOffset(int64_t imm, int32_t instr) {
const int32_t imm32 = static_cast<int32_t>(imm);
const int32_t off = (((imm32 >> 2) << kImm26Shift) & kImm26Mask);
return (instr & ~kImm26Mask) | off;
}

int64_t DecodeImm26BranchOffset(int32_t instr) {
const int32_t off = (((instr & kImm26Mask) >> kImm26Shift) << 6) >> 4;
return static_cast<int64_t>(off);
}

void EmitCompareAndBranchOp(CompareAndBranchOp op,
Register rt,
int64_t imm,
Expand Down
6 changes: 6 additions & 0 deletions runtime/vm/compiler/assembler/assembler_x64.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1781,6 +1781,12 @@ void Assembler::TryAllocateArray(intptr_t cid,
}
}

void Assembler::GenerateUnRelocatedPcRelativeCall() {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
buffer_.Emit<uint8_t>(0xe8);
buffer_.Emit<int32_t>(0x68686868);
}

void Assembler::Align(int alignment, intptr_t offset) {
ASSERT(Utils::IsPowerOfTwo(alignment));
intptr_t pos = offset + buffer_.GetPosition();
Expand Down
13 changes: 13 additions & 0 deletions runtime/vm/compiler/assembler/assembler_x64.h
Original file line number Diff line number Diff line change
Expand Up @@ -897,6 +897,19 @@ class Assembler : public AssemblerBase {
Register end_address,
Register temp);

// This emits an PC-relative call of the form "callq *[rip+<offset>]". The
// offset is not yet known and needs therefore relocation to the right place
// before the code can be used.
//
// The neccessary information for the "linker" (i.e. the relocation
// information) is stored in [RawCode::static_calls_target_table_]: an entry
// of the form
//
// (Code::kPcRelativeCall & pc_offset, <target-code>, <target-function>)
//
// will be used during relocation to fix the offset.
void GenerateUnRelocatedPcRelativeCall();

// Debugging and bringup support.
void Breakpoint() { int3(); }
void Stop(const char* message) override;
Expand Down
6 changes: 6 additions & 0 deletions runtime/vm/compiler/assembler/disassembler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,12 @@ void Disassembler::DisassembleCodeHelper(const char* function_fullname,

const char* skind = nullptr;
switch (kind) {
case Code::kPcRelativeCall:
skind = "pc-relative-call";
break;
case Code::kPcRelativeTailCall:
skind = "pc-relative-tail-call";
break;
case Code::kCallViaCode:
skind = "call-via-code";
break;
Expand Down
23 changes: 17 additions & 6 deletions runtime/vm/compiler/backend/flow_graph_compiler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,19 @@ void FlowGraphCompiler::AddNullCheck(intptr_t pc_offset,
null_check_name_idx);
}

void FlowGraphCompiler::AddPcRelativeCallTarget(const Function& function) {
ASSERT(function.IsZoneHandle());
static_calls_target_table_.Add(new (zone()) StaticCallsStruct(
Code::kPcRelativeCall, assembler()->CodeSize(), &function, NULL));
}

void FlowGraphCompiler::AddPcRelativeCallStubTarget(const Code& stub_code) {
ASSERT(stub_code.IsZoneHandle());
ASSERT(!stub_code.IsNull());
static_calls_target_table_.Add(new (zone()) StaticCallsStruct(
Code::kPcRelativeCall, assembler()->CodeSize(), NULL, &stub_code));
}

void FlowGraphCompiler::AddStaticCallTarget(const Function& func) {
ASSERT(func.IsZoneHandle());
static_calls_target_table_.Add(new (zone()) StaticCallsStruct(
Expand Down Expand Up @@ -1954,9 +1967,8 @@ void FlowGraphCompiler::EmitTestAndCall(const CallTargets& targets,
// Do not use the code from the function, but let the code be patched so
// that we can record the outgoing edges to other code.
const Function& function = *targets.TargetAt(smi_case)->target;
GenerateStaticDartCall(
deopt_id, token_index, *StubCode::CallStaticFunction_entry(),
RawPcDescriptors::kOther, locs, function, entry_kind);
GenerateStaticDartCall(deopt_id, token_index, RawPcDescriptors::kOther,
locs, function, entry_kind);
__ Drop(args_info.count_with_type_args);
if (match_found != NULL) {
__ Jump(match_found);
Expand Down Expand Up @@ -2005,9 +2017,8 @@ void FlowGraphCompiler::EmitTestAndCall(const CallTargets& targets,
// Do not use the code from the function, but let the code be patched so
// that we can record the outgoing edges to other code.
const Function& function = *targets.TargetAt(i)->target;
GenerateStaticDartCall(
deopt_id, token_index, *StubCode::CallStaticFunction_entry(),
RawPcDescriptors::kOther, locs, function, entry_kind);
GenerateStaticDartCall(deopt_id, token_index, RawPcDescriptors::kOther,
locs, function, entry_kind);
__ Drop(args_info.count_with_type_args);
if (!is_last_check || add_megamorphic_call) {
__ Jump(match_found);
Expand Down
3 changes: 2 additions & 1 deletion runtime/vm/compiler/backend/flow_graph_compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,6 @@ class FlowGraphCompiler : public ValueObject {
void GenerateStaticDartCall(
intptr_t deopt_id,
TokenPosition token_pos,
const StubEntry& stub_entry,
RawPcDescriptors::Kind kind,
LocationSummary* locs,
const Function& target,
Expand Down Expand Up @@ -772,6 +771,8 @@ class FlowGraphCompiler : public ValueObject {

void EmitFrameEntry();

void AddPcRelativeCallTarget(const Function& function);
void AddPcRelativeCallStubTarget(const Code& stub_code);
void AddStaticCallTarget(const Function& function);

void GenerateDeferredCode();
Expand Down
3 changes: 1 addition & 2 deletions runtime/vm/compiler/backend/flow_graph_compiler_arm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -988,7 +988,6 @@ void FlowGraphCompiler::GenerateDartCall(intptr_t deopt_id,

void FlowGraphCompiler::GenerateStaticDartCall(intptr_t deopt_id,
TokenPosition token_pos,
const StubEntry& stub_entry,
RawPcDescriptors::Kind kind,
LocationSummary* locs,
const Function& target,
Expand All @@ -997,6 +996,7 @@ void FlowGraphCompiler::GenerateStaticDartCall(intptr_t deopt_id,
// call sites are never patched for breakpoints: the function is deoptimized
// and the unoptimized code with IC calls for static calls is patched instead.
ASSERT(is_optimizing());
const auto& stub_entry = *StubCode::CallStaticFunction_entry();
__ BranchLinkWithEquivalence(stub_entry, target, entry_kind);
EmitCallsiteMetadata(token_pos, deopt_id, kind, locs);
AddStaticCallTarget(target);
Expand Down Expand Up @@ -1163,7 +1163,6 @@ void FlowGraphCompiler::EmitOptimizedStaticCall(
// Do not use the code from the function, but let the code be patched so that
// we can record the outgoing edges to other code.
GenerateStaticDartCall(deopt_id, token_pos,
*StubCode::CallStaticFunction_entry(),
RawPcDescriptors::kOther, locs, function, entry_kind);
__ Drop(count_with_type_args);
}
Expand Down
3 changes: 1 addition & 2 deletions runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc
Original file line number Diff line number Diff line change
Expand Up @@ -966,7 +966,6 @@ void FlowGraphCompiler::GenerateDartCall(intptr_t deopt_id,

void FlowGraphCompiler::GenerateStaticDartCall(intptr_t deopt_id,
TokenPosition token_pos,
const StubEntry& stub_entry,
RawPcDescriptors::Kind kind,
LocationSummary* locs,
const Function& target,
Expand All @@ -976,6 +975,7 @@ void FlowGraphCompiler::GenerateStaticDartCall(intptr_t deopt_id,
// call sites are never patched for breakpoints: the function is deoptimized
// and the unoptimized code with IC calls for static calls is patched instead.
ASSERT(is_optimizing());
const auto& stub_entry = *StubCode::CallStaticFunction_entry();
__ BranchLinkWithEquivalence(stub_entry, target);
EmitCallsiteMetadata(token_pos, deopt_id, kind, locs);
AddStaticCallTarget(target);
Expand Down Expand Up @@ -1146,7 +1146,6 @@ void FlowGraphCompiler::EmitOptimizedStaticCall(
// Do not use the code from the function, but let the code be patched so that
// we can record the outgoing edges to other code.
GenerateStaticDartCall(deopt_id, token_pos,
*StubCode::CallStaticFunction_entry(),
RawPcDescriptors::kOther, locs, function);
__ Drop(count_with_type_args);
}
Expand Down
3 changes: 1 addition & 2 deletions runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
Original file line number Diff line number Diff line change
Expand Up @@ -877,12 +877,12 @@ void FlowGraphCompiler::GenerateDartCall(intptr_t deopt_id,

void FlowGraphCompiler::GenerateStaticDartCall(intptr_t deopt_id,
TokenPosition token_pos,
const StubEntry& stub_entry,
RawPcDescriptors::Kind kind,
LocationSummary* locs,
const Function& target,
Code::EntryKind entry_kind) {
// TODO(sjindel/entrypoints): Support multiple entrypoints on IA32.
const auto& stub_entry = *StubCode::CallStaticFunction_entry();
__ Call(stub_entry, true /* movable_target */);
EmitCallsiteMetadata(token_pos, deopt_id, kind, locs);
AddStaticCallTarget(target);
Expand Down Expand Up @@ -1018,7 +1018,6 @@ void FlowGraphCompiler::EmitOptimizedStaticCall(
// Do not use the code from the function, but let the code be patched so that
// we can record the outgoing edges to other code.
GenerateStaticDartCall(deopt_id, token_pos,
*StubCode::CallStaticFunction_entry(),
RawPcDescriptors::kOther, locs, function);
__ Drop(count_with_type_args);
}
Expand Down
3 changes: 1 addition & 2 deletions runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
Original file line number Diff line number Diff line change
Expand Up @@ -979,7 +979,6 @@ void FlowGraphCompiler::GenerateDartCall(intptr_t deopt_id,

void FlowGraphCompiler::GenerateStaticDartCall(intptr_t deopt_id,
TokenPosition token_pos,
const StubEntry& stub_entry,
RawPcDescriptors::Kind kind,
LocationSummary* locs,
const Function& target,
Expand All @@ -988,6 +987,7 @@ void FlowGraphCompiler::GenerateStaticDartCall(intptr_t deopt_id,
// call sites are never patched for breakpoints: the function is deoptimized
// and the unoptimized code with IC calls for static calls is patched instead.
ASSERT(is_optimizing());
const auto& stub_entry = *StubCode::CallStaticFunction_entry();
__ CallWithEquivalence(stub_entry, target, entry_kind);
EmitCallsiteMetadata(token_pos, deopt_id, kind, locs);
AddStaticCallTarget(target);
Expand Down Expand Up @@ -1140,7 +1140,6 @@ void FlowGraphCompiler::EmitOptimizedStaticCall(
// Do not use the code from the function, but let the code be patched so that
// we can record the outgoing edges to other code.
GenerateStaticDartCall(deopt_id, token_pos,
*StubCode::CallStaticFunction_entry(),
RawPcDescriptors::kOther, locs, function, entry_kind);
__ Drop(count_with_type_args, RCX);
}
Expand Down
16 changes: 16 additions & 0 deletions runtime/vm/instructions_arm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,22 @@ bool ReturnPattern::IsValid() const {
return false;
}

bool PcRelativeCallPattern::IsValid() const {
// bl <offset>
const uint32_t word = *reinterpret_cast<uint32_t*>(pc_);
const uint32_t cond_all = 0xe0;
const uint32_t branch_link = 0x0b;
return (word >> 24) == (cond_all | branch_link);
}

bool PcRelativeJumpPattern::IsValid() const {
// b <offset>
const uint32_t word = *reinterpret_cast<uint32_t*>(pc_);
const uint32_t cond_all = 0xe0;
const uint32_t branch_nolink = 0x0a;
return (word >> 24) == (cond_all | branch_nolink);
}

intptr_t TypeTestingStubCallPattern::GetSubtypeTestCachePoolIndex() {
// Calls to the type testing stubs look like:
// ldr R3, [PP+idx]
Expand Down
60 changes: 60 additions & 0 deletions runtime/vm/instructions_arm.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,66 @@ class ReturnPattern : public ValueObject {
const uword pc_;
};

class PcRelativeCallPattern : public ValueObject {
public:
explicit PcRelativeCallPattern(uword pc) : pc_(pc) {}

static const int kLengthInBytes = 1 * Instr::kInstrSize;

int32_t distance() {
#if !defined(DART_PRECOMPILED_RUNTIME)
return Assembler::DecodeBranchOffset(*reinterpret_cast<int32_t*>(pc_));
#else
UNREACHABLE();
return 0;
#endif
}

void set_distance(int32_t distance) {
#if !defined(DART_PRECOMPILED_RUNTIME)
int32_t* word = reinterpret_cast<int32_t*>(pc_);
*word = Assembler::EncodeBranchOffset(distance, *word);
#else
UNREACHABLE();
#endif
}

bool IsValid() const;

private:
uword pc_;
};

class PcRelativeJumpPattern : public ValueObject {
public:
explicit PcRelativeJumpPattern(uword pc) : pc_(pc) {}

static const int kLengthInBytes = 1 * Instr::kInstrSize;

int32_t distance() {
#if !defined(DART_PRECOMPILED_RUNTIME)
return Assembler::DecodeBranchOffset(*reinterpret_cast<int32_t*>(pc_));
#else
UNREACHABLE();
return 0;
#endif
}

void set_distance(int32_t distance) {
#if !defined(DART_PRECOMPILED_RUNTIME)
int32_t* word = reinterpret_cast<int32_t*>(pc_);
*word = Assembler::EncodeBranchOffset(distance, *word);
#else
UNREACHABLE();
#endif
}

bool IsValid() const;

private:
uword pc_;
};

} // namespace dart

#endif // RUNTIME_VM_INSTRUCTIONS_ARM_H_
Loading

0 comments on commit 9b9a035

Please sign in to comment.