VM: Link native calls lazily.

The first invocation of a native functions goes into LinkNativeCall which
determines the final entry point and patches the object pool entry.

When running precompiled code, this makes deserializing the object
pool entries for native functions easy, they all initially point to
a single entry (LinkNativeCall).

BUG=
R=rmacnak@google.com

Review URL: https://codereview.chromium.org//1294113004 .
This commit is contained in:
Florian Schneider 2015-08-21 11:37:50 +02:00
parent 96a76a9bb1
commit c05f1f9448
23 changed files with 630 additions and 53 deletions

View file

@ -1805,13 +1805,15 @@ class NativeBodyNode : public AstNode {
const String& native_c_function_name,
NativeFunction native_c_function,
LocalScope* scope,
bool is_bootstrap_native)
bool is_bootstrap_native,
bool link_lazily = false)
: AstNode(token_pos),
function_(function),
native_c_function_name_(native_c_function_name),
native_c_function_(native_c_function),
scope_(scope),
is_bootstrap_native_(is_bootstrap_native) {
is_bootstrap_native_(is_bootstrap_native),
link_lazily_(link_lazily) {
ASSERT(function_.IsZoneHandle());
ASSERT(native_c_function_ != NULL);
ASSERT(native_c_function_name_.IsZoneHandle());
@ -1826,6 +1828,8 @@ class NativeBodyNode : public AstNode {
LocalScope* scope() const { return scope_; }
bool is_bootstrap_native() const { return is_bootstrap_native_; }
bool link_lazily() const { return link_lazily_; }
virtual void VisitChildren(AstNodeVisitor* visitor) const { }
DECLARE_COMMON_NODE_FUNCTIONS(NativeBodyNode);
@ -1836,6 +1840,7 @@ class NativeBodyNode : public AstNode {
NativeFunction native_c_function_; // Actual non-Dart implementation.
LocalScope* scope_;
const bool is_bootstrap_native_; // Is a bootstrap native method.
const bool link_lazily_;
DISALLOW_IMPLICIT_CONSTRUCTORS(NativeBodyNode);
};

View file

@ -7,6 +7,7 @@
#define VM_CODE_PATCHER_H_
#include "vm/allocation.h"
#include "vm/native_entry.h"
namespace dart {
@ -88,11 +89,18 @@ class CodePatcher : public AllStatic {
static RawObject* GetEdgeCounterAt(uword pc, const Code& code);
static int32_t GetPoolOffsetAt(uword return_address);
static void SetPoolOffsetAt(uword return_address, int32_t offset);
static void PatchPoolPointerCallAt(uword return_address,
const Code& code,
uword new_target);
static uword GetNativeCallAt(uword return_address,
const Code& code,
NativeFunction* target);
static void PatchNativeCallAt(uword return_address,
const Code& code,
NativeFunction target,
const Code& trampoline);
};
} // namespace dart

View file

@ -78,6 +78,27 @@ RawFunction* CodePatcher::GetUnoptimizedStaticCallAt(
}
void CodePatcher::PatchNativeCallAt(uword return_address,
const Code& code,
NativeFunction target,
const Code& trampoline) {
ASSERT(code.ContainsInstructionAt(return_address));
NativeCallPattern call(return_address, code);
call.set_target(trampoline.EntryPoint());
call.set_native_function(target);
}
uword CodePatcher::GetNativeCallAt(uword return_address,
const Code& code,
NativeFunction* target) {
ASSERT(code.ContainsInstructionAt(return_address));
NativeCallPattern call(return_address, code);
*target = call.native_function();
return call.target();
}
// This class pattern matches on a load from the object pool. Loading on
// ARM is complicated because it can take four possible different forms. We
// match backwards from the end of the sequence so we can reuse the code for

View file

@ -118,6 +118,27 @@ RawFunction* CodePatcher::GetUnoptimizedStaticCallAt(
}
void CodePatcher::PatchNativeCallAt(uword return_address,
const Code& code,
NativeFunction target,
const Code& trampoline) {
ASSERT(code.ContainsInstructionAt(return_address));
NativeCallPattern call(return_address, code);
call.set_target(trampoline.EntryPoint());
call.set_native_function(target);
}
uword CodePatcher::GetNativeCallAt(uword return_address,
const Code& code,
NativeFunction* target) {
ASSERT(code.ContainsInstructionAt(return_address));
NativeCallPattern call(return_address, code);
*target = call.native_function();
return call.target();
}
// This class pattern matches on a load from the object pool. Loading on
// ARM64 is complicated because it can take more than one form. We
// match backwards from the end of the sequence so we can reuse the code for

View file

@ -44,6 +44,8 @@ class UnoptimizedCall : public ValueObject {
void set_target(uword target) const {
uword* target_addr = reinterpret_cast<uword*>(call_address() + 1);
uword offset = target - return_address();
WritableInstructionsScope writable(reinterpret_cast<uword>(target_addr),
sizeof(offset));
*target_addr = offset;
CPU::FlushICache(call_address(), kInstructionSize);
}
@ -64,11 +66,33 @@ class UnoptimizedCall : public ValueObject {
return start_ + 1 * kInstructionSize;
}
protected:
uword start_;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(UnoptimizedCall);
};
class NativeCall : public UnoptimizedCall {
public:
explicit NativeCall(uword return_address) : UnoptimizedCall(return_address) {
}
NativeFunction native_function() const {
return *reinterpret_cast<NativeFunction*>(start_ + 1);
}
void set_native_function(NativeFunction func) const {
WritableInstructionsScope writable(start_ + 1, sizeof(func));
*reinterpret_cast<NativeFunction*>(start_ + 1) = func;
}
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(NativeCall);
};
class InstanceCall : public UnoptimizedCall {
public:
explicit InstanceCall(uword return_address)
@ -210,6 +234,28 @@ RawFunction* CodePatcher::GetUnoptimizedStaticCallAt(
}
void CodePatcher::PatchNativeCallAt(uword return_address,
const Code& code,
NativeFunction target,
const Code& trampoline) {
ASSERT(code.ContainsInstructionAt(return_address));
NativeCall call(return_address);
call.set_target(trampoline.EntryPoint());
call.set_native_function(target);
}
uword CodePatcher::GetNativeCallAt(uword return_address,
const Code& code,
NativeFunction* target) {
ASSERT(code.ContainsInstructionAt(return_address));
NativeCall call(return_address);
*target = call.native_function();
return call.target();
}
intptr_t CodePatcher::InstanceCallSizeInBytes() {
return InstanceCall::kNumInstructions * InstanceCall::kInstructionSize;
}

View file

@ -77,6 +77,27 @@ RawFunction* CodePatcher::GetUnoptimizedStaticCallAt(
}
void CodePatcher::PatchNativeCallAt(uword return_address,
const Code& code,
NativeFunction target,
const Code& trampoline) {
ASSERT(code.ContainsInstructionAt(return_address));
NativeCallPattern call(return_address, code);
call.set_target(trampoline.EntryPoint());
call.set_native_function(target);
}
uword CodePatcher::GetNativeCallAt(uword return_address,
const Code& code,
NativeFunction* target) {
ASSERT(code.ContainsInstructionAt(return_address));
NativeCallPattern call(return_address, code);
*target = call.native_function();
return call.target();
}
// This class pattern matches on a load from the object pool. Loading on
// MIPS is complicated because it can take four possible different forms.
// We match backwards from the end of the sequence so we can reuse the code

View file

@ -23,8 +23,8 @@ namespace dart {
class UnoptimizedCall : public ValueObject {
public:
UnoptimizedCall(uword return_address, const Code& code)
: start_(return_address - kCallPatternSize),
object_pool_(ObjectPool::Handle(code.GetObjectPool())) {
: object_pool_(ObjectPool::Handle(code.GetObjectPool())),
start_(return_address - kCallPatternSize) {
ASSERT(IsValid(return_address));
ASSERT((kCallPatternSize - 7) == Assembler::kCallExternalLabelSize);
}
@ -40,9 +40,12 @@ class UnoptimizedCall : public ValueObject {
(code_bytes[9] == 0x97);
}
intptr_t argument_index() const {
return IndexFromPPLoad(start_ + 3);
}
RawObject* ic_data() const {
intptr_t index = IndexFromPPLoad(start_ + 3);
return object_pool_.ObjectAt(index);
return object_pool_.ObjectAt(argument_index());
}
uword target() const {
@ -56,13 +59,36 @@ class UnoptimizedCall : public ValueObject {
// No need to flush the instruction cache, since the code is not modified.
}
protected:
const ObjectPool& object_pool_;
private:
uword start_;
const ObjectPool& object_pool_;
DISALLOW_IMPLICIT_CONSTRUCTORS(UnoptimizedCall);
};
class NativeCall : public UnoptimizedCall {
public:
NativeCall(uword return_address, const Code& code)
: UnoptimizedCall(return_address, code) {
}
NativeFunction native_function() const {
return reinterpret_cast<NativeFunction>(
object_pool_.RawValueAt(argument_index()));
}
void set_native_function(NativeFunction func) const {
object_pool_.SetRawValueAt(argument_index(),
reinterpret_cast<uword>(func));
}
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(NativeCall);
};
class InstanceCall : public UnoptimizedCall {
public:
InstanceCall(uword return_address, const Code& code)
@ -211,6 +237,27 @@ RawFunction* CodePatcher::GetUnoptimizedStaticCallAt(
}
void CodePatcher::PatchNativeCallAt(uword return_address,
const Code& code,
NativeFunction target,
const Code& trampoline) {
ASSERT(code.ContainsInstructionAt(return_address));
NativeCall call(return_address, code);
call.set_target(trampoline.EntryPoint());
call.set_native_function(target);
}
uword CodePatcher::GetNativeCallAt(uword return_address,
const Code& code,
NativeFunction* target) {
ASSERT(code.ContainsInstructionAt(return_address));
NativeCall call(return_address, code);
*target = call.native_function();
return call.target();
}
// The expected code pattern of an edge counter in unoptimized code:
// 49 8b 87 imm32 mov RAX, [PP + offset]
class EdgeCounter : public ValueObject {

View file

@ -107,7 +107,7 @@ DEFINE_FLAG_HANDLER(NooptModeHandler,
DECLARE_FLAG(bool, lazy_dispatchers);
DECLARE_FLAG(bool, interpret_irregexp);
DECLARE_FLAG(bool, enable_mirrors);
DECLARE_FLAG(bool, link_natives_lazily);
static void PrecompileModeHandler(bool value) {
if (value) {
@ -116,6 +116,7 @@ static void PrecompileModeHandler(bool value) {
FLAG_interpret_irregexp = true;
FLAG_enable_mirrors = false;
FLAG_precompile_collect_closures = true;
FLAG_link_natives_lazily = true;
}
}

View file

@ -43,6 +43,51 @@ int CallPattern::LengthInBytes() {
}
NativeCallPattern::NativeCallPattern(uword pc, const Code& code)
: object_pool_(ObjectPool::Handle(code.GetObjectPool())),
end_(pc),
native_function_pool_index_(-1),
target_address_pool_index_(-1) {
ASSERT(code.ContainsInstructionAt(pc));
// Last instruction: blx lr.
ASSERT(*(reinterpret_cast<uword*>(end_) - 1) == 0xe12fff3e);
Register reg;
uword native_function_load_end =
InstructionPattern::DecodeLoadWordFromPool(end_ - Instr::kInstrSize,
&reg,
&target_address_pool_index_);
ASSERT(reg == LR);
InstructionPattern::DecodeLoadWordFromPool(native_function_load_end,
&reg,
&native_function_pool_index_);
ASSERT(reg == R5);
}
uword NativeCallPattern::target() const {
return object_pool_.RawValueAt(target_address_pool_index_);
}
void NativeCallPattern::set_target(uword target_address) const {
object_pool_.SetRawValueAt(target_address_pool_index_, target_address);
// No need to flush the instruction cache, since the code is not modified.
}
NativeFunction NativeCallPattern::native_function() const {
return reinterpret_cast<NativeFunction>(
object_pool_.RawValueAt(native_function_pool_index_));
}
void NativeCallPattern::set_native_function(NativeFunction func) const {
object_pool_.SetRawValueAt(native_function_pool_index_,
reinterpret_cast<uword>(func));
}
// Decodes a load sequence ending at 'end' (the last instruction of the load
// sequence is the instruction before the one at end). Returns a pointer to
// the first instruction in the sequence. Returns the register being loaded

View file

@ -11,6 +11,7 @@
#endif
#include "vm/constants_arm.h"
#include "vm/native_entry.h"
#include "vm/object.h"
namespace dart {
@ -75,6 +76,27 @@ class CallPattern : public ValueObject {
};
class NativeCallPattern : public ValueObject {
public:
NativeCallPattern(uword pc, const Code& code);
uword target() const;
void set_target(uword target_address) const;
NativeFunction native_function() const;
void set_native_function(NativeFunction target) const;
private:
const ObjectPool& object_pool_;
uword end_;
intptr_t native_function_pool_index_;
intptr_t target_address_pool_index_;
DISALLOW_COPY_AND_ASSIGN(NativeCallPattern);
};
class JumpPattern : public ValueObject {
public:
JumpPattern(uword pc, const Code& code);

View file

@ -32,6 +32,51 @@ CallPattern::CallPattern(uword pc, const Code& code)
}
NativeCallPattern::NativeCallPattern(uword pc, const Code& code)
: object_pool_(ObjectPool::Handle(code.GetObjectPool())),
end_(pc),
native_function_pool_index_(-1),
target_address_pool_index_(-1) {
ASSERT(code.ContainsInstructionAt(pc));
// Last instruction: blr ip0.
ASSERT(*(reinterpret_cast<uint32_t*>(end_) - 1) == 0xd63f0200);
Register reg;
uword native_function_load_end =
InstructionPattern::DecodeLoadWordFromPool(end_ - Instr::kInstrSize,
&reg,
&target_address_pool_index_);
ASSERT(reg == IP0);
InstructionPattern::DecodeLoadWordFromPool(native_function_load_end,
&reg,
&native_function_pool_index_);
ASSERT(reg == R5);
}
uword NativeCallPattern::target() const {
return object_pool_.RawValueAt(target_address_pool_index_);
}
void NativeCallPattern::set_target(uword target_address) const {
object_pool_.SetRawValueAt(target_address_pool_index_, target_address);
// No need to flush the instruction cache, since the code is not modified.
}
NativeFunction NativeCallPattern::native_function() const {
return reinterpret_cast<NativeFunction>(
object_pool_.RawValueAt(native_function_pool_index_));
}
void NativeCallPattern::set_native_function(NativeFunction func) const {
object_pool_.SetRawValueAt(native_function_pool_index_,
reinterpret_cast<uword>(func));
}
intptr_t InstructionPattern::OffsetFromPPIndex(intptr_t index) {
return Array::element_offset(index);
}

View file

@ -11,6 +11,7 @@
#endif
#include "vm/constants_arm64.h"
#include "vm/native_entry.h"
#include "vm/object.h"
namespace dart {
@ -82,6 +83,27 @@ class CallPattern : public ValueObject {
};
class NativeCallPattern : public ValueObject {
public:
NativeCallPattern(uword pc, const Code& code);
uword target() const;
void set_target(uword target_address) const;
NativeFunction native_function() const;
void set_native_function(NativeFunction target) const;
private:
const ObjectPool& object_pool_;
uword end_;
intptr_t native_function_pool_index_;
intptr_t target_address_pool_index_;
DISALLOW_COPY_AND_ASSIGN(NativeCallPattern);
};
class JumpPattern : public ValueObject {
public:
JumpPattern(uword pc, const Code& code);

View file

@ -145,6 +145,51 @@ void CallPattern::SetTargetAddress(uword target_address) const {
}
NativeCallPattern::NativeCallPattern(uword pc, const Code& code)
: object_pool_(ObjectPool::Handle(code.GetObjectPool())),
end_(pc),
native_function_pool_index_(-1),
target_address_pool_index_(-1) {
ASSERT(code.ContainsInstructionAt(pc));
// Last instruction: jalr RA, T9(=R25).
ASSERT(*(reinterpret_cast<uword*>(end_) - 2) == 0x0320f809);
Register reg;
uword native_function_load_end =
InstructionPattern::DecodeLoadWordFromPool(end_ - 2 * Instr::kInstrSize,
&reg,
&target_address_pool_index_);
ASSERT(reg == T9);
InstructionPattern::DecodeLoadWordFromPool(native_function_load_end,
&reg,
&native_function_pool_index_);
ASSERT(reg == T5);
}
uword NativeCallPattern::target() const {
return object_pool_.RawValueAt(target_address_pool_index_);
}
void NativeCallPattern::set_target(uword target_address) const {
object_pool_.SetRawValueAt(target_address_pool_index_, target_address);
// No need to flush the instruction cache, since the code is not modified.
}
NativeFunction NativeCallPattern::native_function() const {
return reinterpret_cast<NativeFunction>(
object_pool_.RawValueAt(native_function_pool_index_));
}
void NativeCallPattern::set_native_function(NativeFunction func) const {
object_pool_.SetRawValueAt(native_function_pool_index_,
reinterpret_cast<uword>(func));
}
void CallPattern::InsertAt(uword pc, uword target_address) {
Instr* lui = Instr::At(pc + (0 * Instr::kInstrSize));
Instr* ori = Instr::At(pc + (1 * Instr::kInstrSize));

View file

@ -11,6 +11,7 @@
#endif
#include "vm/constants_mips.h"
#include "vm/native_entry.h"
#include "vm/object.h"
namespace dart {
@ -75,6 +76,27 @@ class CallPattern : public ValueObject {
};
class NativeCallPattern : public ValueObject {
public:
NativeCallPattern(uword pc, const Code& code);
uword target() const;
void set_target(uword target_address) const;
NativeFunction native_function() const;
void set_native_function(NativeFunction target) const;
private:
const ObjectPool& object_pool_;
uword end_;
intptr_t native_function_pool_index_;
intptr_t target_address_pool_index_;
DISALLOW_COPY_AND_ASSIGN(NativeCallPattern);
};
class JumpPattern : public ValueObject {
public:
JumpPattern(uword pc, const Code& code);

View file

@ -3409,6 +3409,10 @@ class NativeCallInstr : public TemplateDefinition<0, Throws> {
return ast_node_.is_bootstrap_native();
}
bool link_lazily() const {
return ast_node_.link_lazily();
}
virtual void PrintOperandsTo(BufferFormatter* f) const;
virtual bool CanDeoptimize() const { return false; }

View file

@ -937,32 +937,42 @@ void NativeCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
// Compute the effective address. When running under the simulator,
// this is a redirection address that forces the simulator to call
// into the runtime system.
uword entry = reinterpret_cast<uword>(native_c_function());
uword entry;
const intptr_t argc_tag = NativeArguments::ComputeArgcTag(function());
const bool is_leaf_call =
(argc_tag & NativeArguments::AutoSetupScopeMask()) == 0;
const StubEntry* stub_entry = NULL;
if (is_bootstrap_native() || is_leaf_call) {
const StubEntry* stub_entry;
if (link_lazily()) {
stub_entry = StubCode::CallBootstrapCFunction_entry();
entry = reinterpret_cast<uword>(&NativeEntry::LinkNativeCall);
#if defined(USING_SIMULATOR)
entry = Simulator::RedirectExternalReference(
entry, Simulator::kBootstrapNativeCall, function().NumParameters());
#endif
} else {
// In the case of non bootstrap native methods the CallNativeCFunction
// stub generates the redirection address when running under the simulator
// and hence we do not change 'entry' here.
stub_entry = StubCode::CallNativeCFunction_entry();
entry = reinterpret_cast<uword>(native_c_function());
if (is_bootstrap_native() || is_leaf_call) {
stub_entry = StubCode::CallBootstrapCFunction_entry();
#if defined(USING_SIMULATOR)
if (!function().IsNativeAutoSetupScope()) {
entry = Simulator::RedirectExternalReference(
entry, Simulator::kBootstrapNativeCall, function().NumParameters());
}
#endif
} else {
// In the case of non bootstrap native methods the CallNativeCFunction
// stub generates the redirection address when running under the simulator
// and hence we do not change 'entry' here.
stub_entry = StubCode::CallNativeCFunction_entry();
#if defined(USING_SIMULATOR)
if (!function().IsNativeAutoSetupScope()) {
entry = Simulator::RedirectExternalReference(
entry, Simulator::kBootstrapNativeCall, function().NumParameters());
}
#endif
}
}
ExternalLabel label(entry);
__ LoadExternalLabel(R5, &label, kNotPatchable);
__ LoadImmediate(R1, argc_tag);
ExternalLabel label(entry);
__ LoadExternalLabel(R5, &label, link_lazily() ? kPatchable : kNotPatchable);
compiler->GenerateCall(token_pos(),
*stub_entry,
RawPcDescriptors::kOther,

View file

@ -790,32 +790,42 @@ void NativeCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
// Compute the effective address. When running under the simulator,
// this is a redirection address that forces the simulator to call
// into the runtime system.
uword entry = reinterpret_cast<uword>(native_c_function());
uword entry;
const intptr_t argc_tag = NativeArguments::ComputeArgcTag(function());
const bool is_leaf_call =
(argc_tag & NativeArguments::AutoSetupScopeMask()) == 0;
const StubEntry* stub_entry = NULL;
if (is_bootstrap_native() || is_leaf_call) {
const StubEntry* stub_entry;
if (link_lazily()) {
stub_entry = StubCode::CallBootstrapCFunction_entry();
entry = reinterpret_cast<uword>(&NativeEntry::LinkNativeCall);
#if defined(USING_SIMULATOR)
entry = Simulator::RedirectExternalReference(
entry, Simulator::kBootstrapNativeCall, function().NumParameters());
#endif
} else {
// In the case of non bootstrap native methods the CallNativeCFunction
// stub generates the redirection address when running under the simulator
// and hence we do not change 'entry' here.
stub_entry = StubCode::CallNativeCFunction_entry();
entry = reinterpret_cast<uword>(native_c_function());
if (is_bootstrap_native() || is_leaf_call) {
stub_entry = StubCode::CallBootstrapCFunction_entry();
#if defined(USING_SIMULATOR)
if (!function().IsNativeAutoSetupScope()) {
entry = Simulator::RedirectExternalReference(
entry, Simulator::kBootstrapNativeCall, function().NumParameters());
}
#endif
} else {
// In the case of non bootstrap native methods the CallNativeCFunction
// stub generates the redirection address when running under the simulator
// and hence we do not change 'entry' here.
stub_entry = StubCode::CallNativeCFunction_entry();
#if defined(USING_SIMULATOR)
if (!function().IsNativeAutoSetupScope()) {
entry = Simulator::RedirectExternalReference(
entry, Simulator::kBootstrapNativeCall, function().NumParameters());
}
#endif
}
}
__ LoadImmediate(R1, argc_tag);
ExternalLabel label(entry);
__ LoadExternalLabel(R5, &label);
__ LoadImmediate(R1, argc_tag);
compiler->GenerateCall(token_pos(),
*stub_entry,
RawPcDescriptors::kOther,

View file

@ -828,11 +828,19 @@ void NativeCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
} else {
__ leal(EAX, Address(EBP, kFirstLocalSlotFromFp * kWordSize));
}
__ movl(ECX, Immediate(reinterpret_cast<uword>(native_c_function())));
__ movl(EDX, Immediate(argc_tag));
const StubEntry* stub_entry = (is_bootstrap_native() || is_leaf_call) ?
StubCode::CallBootstrapCFunction_entry() :
StubCode::CallNativeCFunction_entry();
const StubEntry* stub_entry;
if (link_lazily()) {
stub_entry = StubCode::CallBootstrapCFunction_entry();
__ movl(ECX, Immediate(NativeEntry::LinkNativeCallLabel().address()));
} else {
stub_entry = (is_bootstrap_native() || is_leaf_call) ?
StubCode::CallBootstrapCFunction_entry() :
StubCode::CallNativeCFunction_entry();
const ExternalLabel label(reinterpret_cast<uword>(native_c_function()));
__ movl(ECX, Immediate(label.address()));
}
compiler->GenerateCall(token_pos(),
*stub_entry,
RawPcDescriptors::kOther,

View file

@ -988,32 +988,42 @@ void NativeCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
// Compute the effective address. When running under the simulator,
// this is a redirection address that forces the simulator to call
// into the runtime system.
uword entry = reinterpret_cast<uword>(native_c_function());
uword entry;
const intptr_t argc_tag = NativeArguments::ComputeArgcTag(function());
const bool is_leaf_call =
(argc_tag & NativeArguments::AutoSetupScopeMask()) == 0;
const StubEntry* stub_entry = NULL;
if (is_bootstrap_native() || is_leaf_call) {
const StubEntry* stub_entry;
if (link_lazily()) {
stub_entry = StubCode::CallBootstrapCFunction_entry();
entry = reinterpret_cast<uword>(&NativeEntry::LinkNativeCall);
#if defined(USING_SIMULATOR)
entry = Simulator::RedirectExternalReference(
entry, Simulator::kBootstrapNativeCall, function().NumParameters());
#endif
} else {
// In the case of non bootstrap native methods the CallNativeCFunction
// stub generates the redirection address when running under the simulator
// and hence we do not change 'entry' here.
stub_entry = StubCode::CallNativeCFunction_entry();
entry = reinterpret_cast<uword>(native_c_function());
if (is_bootstrap_native() || is_leaf_call) {
stub_entry = StubCode::CallBootstrapCFunction_entry();
#if defined(USING_SIMULATOR)
if (!function().IsNativeAutoSetupScope()) {
entry = Simulator::RedirectExternalReference(
entry, Simulator::kBootstrapNativeCall, function().NumParameters());
}
#endif
} else {
// In the case of non bootstrap native methods the CallNativeCFunction
// stub generates the redirection address when running under the simulator
// and hence we do not change 'entry' here.
stub_entry = StubCode::CallNativeCFunction_entry();
#if defined(USING_SIMULATOR)
if (!function().IsNativeAutoSetupScope()) {
entry = Simulator::RedirectExternalReference(
entry, Simulator::kBootstrapNativeCall, function().NumParameters());
}
#endif
}
}
__ LoadImmediate(A1, argc_tag);
ExternalLabel label(entry);
__ LoadExternalLabel(T5, &label, kNotPatchable);
__ LoadImmediate(A1, argc_tag);
compiler->GenerateCall(token_pos(),
*stub_entry,
RawPcDescriptors::kOther,

View file

@ -781,14 +781,21 @@ void NativeCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
} else {
__ leaq(RAX, Address(RBP, kFirstLocalSlotFromFp * kWordSize));
}
ExternalLabel label(reinterpret_cast<uword>(native_c_function()));
__ LoadExternalLabel(RBX, &label, kNotPatchable);
__ LoadImmediate(R10, Immediate(argc_tag));
const StubEntry& stub_entry = (is_bootstrap_native() || is_leaf_call) ?
*StubCode::CallBootstrapCFunction_entry() :
*StubCode::CallNativeCFunction_entry();
const StubEntry* stub_entry;
if (link_lazily()) {
stub_entry = StubCode::CallBootstrapCFunction_entry();
__ LoadExternalLabel(
RBX, &NativeEntry::LinkNativeCallLabel(), kPatchable);
} else {
stub_entry = (is_bootstrap_native() || is_leaf_call)
? StubCode::CallBootstrapCFunction_entry()
: StubCode::CallNativeCFunction_entry();
const ExternalLabel label(reinterpret_cast<uword>(native_c_function()));
__ LoadExternalLabel(RBX, &label, kNotPatchable);
}
compiler->GenerateCall(token_pos(),
stub_entry,
*stub_entry,
RawPcDescriptors::kOther,
locs());
__ popq(result);

View file

@ -6,10 +6,14 @@
#include "include/dart_api.h"
#include "vm/bootstrap.h"
#include "vm/code_patcher.h"
#include "vm/dart_api_impl.h"
#include "vm/dart_api_state.h"
#include "vm/object_store.h"
#include "vm/reusable_handles.h"
#include "vm/stack_frame.h"
#include "vm/symbols.h"
#include "vm/tags.h"
@ -23,6 +27,10 @@ static ExternalLabel native_call_label(
reinterpret_cast<uword>(&NativeEntry::NativeCallWrapper));
static ExternalLabel link_native_call_label(
reinterpret_cast<uword>(&NativeEntry::LinkNativeCall));
NativeFunction NativeEntry::ResolveNative(const Library& library,
const String& function_name,
int number_of_arguments,
@ -123,4 +131,147 @@ void NativeEntry::NativeCallWrapper(Dart_NativeArguments args,
VERIFY_ON_TRANSITION;
}
static bool IsNativeKeyword(const TokenStream::Iterator& it) {
return Token::IsIdentifier(it.CurrentTokenKind()) &&
(it.CurrentLiteral() == Symbols::Native().raw());
}
static NativeFunction ResolveNativeFunction(Isolate *isolate,
const Function& func,
bool* is_bootstrap_native) {
const Script& script = Script::Handle(isolate, func.script());
const Class& cls = Class::Handle(isolate, func.Owner());
const Library& library = Library::Handle(isolate, cls.library());
*is_bootstrap_native =
Bootstrap::IsBootstapResolver(library.native_entry_resolver());
TokenStream::Iterator it(TokenStream::Handle(isolate, script.tokens()),
func.token_pos());
const intptr_t end_pos = func.end_token_pos();
while (!IsNativeKeyword(it) && it.CurrentPosition() <= end_pos) {
it.Advance();
}
ASSERT(IsNativeKeyword(it));
it.Advance();
ASSERT(it.CurrentTokenKind() == Token::kSTRING);
const String& native_name = String::Handle(it.CurrentLiteral());
const int num_params = NativeArguments::ParameterCountForResolution(func);
bool auto_setup_scope = true;
return NativeEntry::ResolveNative(
library, native_name, num_params, &auto_setup_scope);
}
const ExternalLabel& NativeEntry::LinkNativeCallLabel() {
return link_native_call_label;
}
void NativeEntry::LinkNativeCall(Dart_NativeArguments args) {
CHECK_STACK_ALIGNMENT;
VERIFY_ON_TRANSITION;
NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args);
/* Tell MemorySanitizer 'arguments' is initialized by generated code. */
MSAN_UNPOISON(arguments, sizeof(*arguments));
TRACE_NATIVE_CALL("%s", "LinkNative");
NativeFunction target_function = NULL;
bool call_through_wrapper = false;
#ifdef USING_SIMULATOR
bool is_native_auto_setup_scope = false;
intptr_t num_parameters = -1;
#endif
{
StackZone zone(arguments->thread());
DartFrameIterator iterator;
StackFrame* caller_frame = iterator.NextFrame();
const Code& code = Code::Handle(caller_frame->LookupDartCode());
const Function& func = Function::Handle(code.function());
#ifdef USING_SIMULATOR
is_native_auto_setup_scope = func.IsNativeAutoSetupScope();
num_parameters = func.NumParameters();
#endif
if (FLAG_trace_natives) {
OS::Print("Resolving native target for %s\n", func.ToCString());
}
bool is_bootstrap_native = false;
target_function = ResolveNativeFunction(
arguments->thread()->isolate(), func, &is_bootstrap_native);
ASSERT(target_function != NULL);
#if defined(DEBUG)
{
NativeFunction current_function = NULL;
uword current_trampoline =
CodePatcher::GetNativeCallAt(caller_frame->pc(),
code,
&current_function);
#if !defined(USING_SIMULATOR)
ASSERT(current_function ==
reinterpret_cast<NativeFunction>(LinkNativeCall));
#else
ASSERT(current_function ==
reinterpret_cast<NativeFunction>(
Simulator::RedirectExternalReference(
reinterpret_cast<uword>(LinkNativeCall),
Simulator::kBootstrapNativeCall,
func.NumParameters())));
#endif
ASSERT(current_trampoline ==
StubCode::CallBootstrapCFunction_entry()->EntryPoint());
}
#endif
const intptr_t argc_tag = NativeArguments::ComputeArgcTag(func);
const bool is_leaf_call =
(argc_tag & NativeArguments::AutoSetupScopeMask()) == 0;
call_through_wrapper = !is_bootstrap_native && !is_leaf_call;
const Code& trampoline = Code::Handle(call_through_wrapper ?
StubCode::CallNativeCFunction_entry()->code() :
StubCode::CallBootstrapCFunction_entry()->code());
NativeFunction patch_target_function = target_function;
#if defined(USING_SIMULATOR)
if (!call_through_wrapper || !is_native_auto_setup_scope) {
patch_target_function = reinterpret_cast<NativeFunction>(
Simulator::RedirectExternalReference(
reinterpret_cast<uword>(patch_target_function),
Simulator::kBootstrapNativeCall, num_parameters));
}
#endif
CodePatcher::PatchNativeCallAt(
caller_frame->pc(), code, patch_target_function, trampoline);
if (FLAG_trace_natives) {
OS::Print(" -> %p (%s, %s)\n",
target_function,
is_bootstrap_native ? "bootstrap" : "non-bootstrap",
is_leaf_call ? "leaf" : "non-leaf");
}
}
VERIFY_ON_TRANSITION;
// Tail-call resolved target.
if (call_through_wrapper) {
NativeEntry::NativeCallWrapper(
args, reinterpret_cast<Dart_NativeFunction>(target_function));
} else {
target_function(arguments);
}
}
} // namespace dart

View file

@ -112,9 +112,13 @@ class NativeEntry : public AllStatic {
static const uint8_t* ResolveSymbolInLibrary(const Library& library,
uword pc);
static const uint8_t* ResolveSymbol(uword pc);
static const ExternalLabel& NativeCallWrapperLabel();
static void NativeCallWrapper(Dart_NativeArguments args,
Dart_NativeFunction func);
static const ExternalLabel& NativeCallWrapperLabel();
static const ExternalLabel& LinkNativeCallLabel();
static void LinkNativeCall(Dart_NativeArguments args);
};
} // namespace dart

View file

@ -45,6 +45,7 @@ DEFINE_FLAG(bool, load_deferred_eagerly, false,
DEFINE_FLAG(bool, trace_parser, false, "Trace parser operations.");
DEFINE_FLAG(bool, supermixin, false, "Allow super calls in mixins.");
DEFINE_FLAG(bool, warn_mixin_typedef, true, "Warning on legacy mixin typedef.");
DEFINE_FLAG(bool, link_natives_lazily, false, "Link native calls lazily");
DECLARE_FLAG(bool, lazy_dispatchers);
DECLARE_FLAG(bool, load_deferred_eagerly);
@ -7333,7 +7334,8 @@ void Parser::ParseNativeFunctionBlock(const ParamList* params,
native_name,
native_function,
current_block_->scope,
is_bootstrap_native)));
is_bootstrap_native,
FLAG_link_natives_lazily)));
}